001package io.ebean.config;
002
003import io.ebean.annotation.Platform;
004import io.ebean.config.dbplatform.DbType;
005import io.ebean.config.dbplatform.IdType;
006import io.ebean.util.StringHelper;
007
008import java.util.ArrayList;
009import java.util.List;
010import java.util.Map;
011import java.util.Map.Entry;
012
013/**
014 * Configuration for DB types such as UUID, Geometry etc.
015 */
016public class PlatformConfig {
017
018  private boolean allQuotedIdentifiers;
019
020  /**
021   * Set this to true for Postgres FOR UPDATE to use NO KEY option.
022   */
023  private boolean forUpdateNoKey;
024
025  private DbConstraintNaming constraintNaming;
026
027  /**
028   * Flag set when a supplied constraintNaming is used.
029   */
030  private boolean customConstraintNaming;
031
032  /**
033   * The database boolean true value (typically either 1, T, or Y).
034   */
035  private String databaseBooleanTrue;
036
037  /**
038   * The database boolean false value (typically either 0, F or N).
039   */
040  private String databaseBooleanFalse;
041
042  /**
043   * For DB's using sequences this is the number of sequence values prefetched.
044   */
045  private int databaseSequenceBatchSize = 20;
046
047  /**
048   * Set for DB's that support both Sequence and Identity (and the default choice is not desired).
049   */
050  private IdType idType;
051
052  /**
053   * The Geometry SRID value (default 4326).
054   */
055  private int geometrySRID = 4326;
056
057  /**
058   * Setting to indicate if UUID should be stored as binary(16) or varchar(40) or native DB type (for H2 and Postgres).
059   */
060  private DbUuid dbUuid = DbUuid.AUTO_VARCHAR;
061
062  /**
063   * Set to true to force InetAddress to map to Varchar (for Postgres rather than INET)
064   */
065  private boolean databaseInetAddressVarchar;
066
067  private boolean caseSensitiveCollation = true;
068
069  private boolean useMigrationStoredProcedures = false;
070
071  /**
072   * Modify the default mapping of standard types such as default precision for DECIMAL etc.
073   */
074  private List<CustomDbTypeMapping> customDbTypeMappings = new ArrayList<>();
075
076  /**
077   * Construct with defaults.
078   */
079  public PlatformConfig() {
080    this.constraintNaming = new DbConstraintNaming();
081  }
082
083  /**
084   * Construct based on given config - typically for DbMigration generation with many platforms.
085   */
086  public PlatformConfig(PlatformConfig platformConfig) {
087    this.forUpdateNoKey = platformConfig.forUpdateNoKey;
088    this.databaseBooleanFalse = platformConfig.databaseBooleanFalse;
089    this.databaseBooleanTrue = platformConfig.databaseBooleanTrue;
090    this.databaseSequenceBatchSize = platformConfig.databaseSequenceBatchSize;
091    this.idType = platformConfig.idType;
092    this.geometrySRID = platformConfig.geometrySRID;
093    this.dbUuid = platformConfig.dbUuid;
094    this.caseSensitiveCollation = platformConfig.caseSensitiveCollation;
095    this.useMigrationStoredProcedures = platformConfig.useMigrationStoredProcedures;
096    this.allQuotedIdentifiers = platformConfig.allQuotedIdentifiers;
097    this.databaseInetAddressVarchar = platformConfig.databaseInetAddressVarchar;
098    this.customDbTypeMappings = platformConfig.customDbTypeMappings;
099    this.constraintNaming = new DbConstraintNaming(!allQuotedIdentifiers);
100  }
101
102  public DbConstraintNaming getConstraintNaming() {
103    return constraintNaming;
104  }
105
106  /**
107   * Set a custom database constraint naming convention.
108   */
109  public void setConstraintNaming(DbConstraintNaming constraintNaming) {
110    this.customConstraintNaming = true;
111    this.constraintNaming = constraintNaming;
112  }
113
114  /**
115   * Return true if all DB column and table names should use quoted identifiers.
116   */
117  public boolean isAllQuotedIdentifiers() {
118    return allQuotedIdentifiers;
119  }
120
121  /**
122   * Set to true if all DB column and table names should use quoted identifiers.
123   * <p>
124   * For Postgres pgjdbc version 42.3.0 should be used with datasource property
125   * <em>quoteReturningIdentifiers</em> set to <em>false</em> (refer #2303).
126   */
127  public void setAllQuotedIdentifiers(boolean allQuotedIdentifiers) {
128    this.allQuotedIdentifiers = allQuotedIdentifiers;
129    if (!customConstraintNaming) {
130      this.constraintNaming = new DbConstraintNaming(!allQuotedIdentifiers);
131    }
132  }
133
134  /**
135   * Return true if the collation is case sensitive.
136   */
137  public boolean isCaseSensitiveCollation() {
138    return caseSensitiveCollation;
139  }
140
141  /**
142   * Set to false to indicate that the collation is case insensitive.
143   */
144  public void setCaseSensitiveCollation(boolean caseSensitiveCollation) {
145    this.caseSensitiveCollation = caseSensitiveCollation;
146  }
147
148  /**
149   * Return true if force use of helper stored procedures for migrations.
150   */
151  public boolean isUseMigrationStoredProcedures() {
152    return useMigrationStoredProcedures;
153  }
154
155  /**
156   * Set true to force use of helper stored procedures for migrations.
157   */
158  public void setUseMigrationStoredProcedures(boolean useMigrationStoredProcedures) {
159    this.useMigrationStoredProcedures = useMigrationStoredProcedures;
160  }
161
162  /**
163   * Return true if Postgres FOR UPDATE should use the NO KEY option.
164   */
165  public boolean isForUpdateNoKey() {
166    return forUpdateNoKey;
167  }
168
169  /**
170   * Set to true such that Postgres FOR UPDATE should use the NO KEY option.
171   */
172  public void setForUpdateNoKey(boolean forUpdateNoKey) {
173    this.forUpdateNoKey = forUpdateNoKey;
174  }
175
176  /**
177   * Return a value used to represent TRUE in the database.
178   * <p>
179   * This is used for databases that do not support boolean natively.
180   * <p>
181   * The value returned is either a Integer or a String (e.g. "1", or "T").
182   */
183  public String getDatabaseBooleanTrue() {
184    return databaseBooleanTrue;
185  }
186
187  /**
188   * Set the value to represent TRUE in the database.
189   * <p>
190   * This is used for databases that do not support boolean natively.
191   * <p>
192   * The value set is either a Integer or a String (e.g. "1", or "T").
193   */
194  public void setDatabaseBooleanTrue(String databaseBooleanTrue) {
195    this.databaseBooleanTrue = databaseBooleanTrue;
196  }
197
198  /**
199   * Return a value used to represent FALSE in the database.
200   */
201  public String getDatabaseBooleanFalse() {
202    return databaseBooleanFalse;
203  }
204
205  /**
206   * Set the value used to represent FALSE in the database.
207   */
208  public void setDatabaseBooleanFalse(String databaseBooleanFalse) {
209    this.databaseBooleanFalse = databaseBooleanFalse;
210  }
211
212  /**
213   * Return the number of DB sequence values that should be preallocated.
214   */
215  public int getDatabaseSequenceBatchSize() {
216    return databaseSequenceBatchSize;
217  }
218
219  /**
220   * Set the number of DB sequence values that should be preallocated.
221   */
222  public void setDatabaseSequenceBatchSize(int databaseSequenceBatchSize) {
223    this.databaseSequenceBatchSize = databaseSequenceBatchSize;
224  }
225
226  /**
227   * Return the Geometry SRID.
228   */
229  public int getGeometrySRID() {
230    return geometrySRID;
231  }
232
233  /**
234   * Set the Geometry SRID.
235   */
236  public void setGeometrySRID(int geometrySRID) {
237    this.geometrySRID = geometrySRID;
238  }
239
240  /**
241   * Return the DB type used to store UUID.
242   */
243  public DbUuid getDbUuid() {
244    return dbUuid;
245  }
246
247  /**
248   * Set the DB type used to store UUID.
249   */
250  public void setDbUuid(DbUuid dbUuid) {
251    this.dbUuid = dbUuid;
252  }
253
254  /**
255   * Return the IdType to use (or null for the default choice).
256   */
257  public IdType getIdType() {
258    return idType;
259  }
260
261  /**
262   * Set the IdType to use (when the DB supports both SEQUENCE and IDENTITY and the default is not desired).
263   */
264  public void setIdType(IdType idType) {
265    this.idType = idType;
266  }
267
268  /**
269   * Return true if InetAddress should map to varchar column (rather than Postgres INET).
270   */
271  public boolean isDatabaseInetAddressVarchar() {
272    return databaseInetAddressVarchar;
273  }
274
275  /**
276   * Set to true to force InetAddress to map to varchar column.
277   */
278  public void setDatabaseInetAddressVarchar(boolean databaseInetAddressVarchar) {
279    this.databaseInetAddressVarchar = databaseInetAddressVarchar;
280  }
281
282  /**
283   * Add a custom type mapping.
284   * <pre>{@code
285   *
286   *   // set the default mapping for BigDecimal.class/decimal
287   *   config.addCustomMapping(DbType.DECIMAL, "decimal(18,6)");
288   *
289   *   // set the default mapping for String.class/varchar but only for Postgres
290   *   config.addCustomMapping(DbType.VARCHAR, "text", Platform.POSTGRES);
291   *
292   * }</pre>
293   *
294   * @param type             The DB type this mapping should apply to
295   * @param columnDefinition The column definition that should be used
296   * @param platform         Optionally specify the platform this mapping should apply to.
297   */
298  public void addCustomMapping(DbType type, String columnDefinition, Platform platform) {
299    customDbTypeMappings.add(new CustomDbTypeMapping(type, columnDefinition, platform));
300  }
301
302  /**
303   * Add a custom type mapping that applies to all platforms.
304   * <pre>{@code
305   *
306   *   // set the default mapping for BigDecimal/decimal
307   *   config.addCustomMapping(DbType.DECIMAL, "decimal(18,6)");
308   *
309   *   // set the default mapping for String/varchar
310   *   config.addCustomMapping(DbType.VARCHAR, "text");
311   *
312   * }</pre>
313   *
314   * @param type             The DB type this mapping should apply to
315   * @param columnDefinition The column definition that should be used
316   */
317  public void addCustomMapping(DbType type, String columnDefinition) {
318    customDbTypeMappings.add(new CustomDbTypeMapping(type, columnDefinition));
319  }
320
321  /**
322   * Return the list of custom type mappings.
323   */
324  public List<CustomDbTypeMapping> getCustomTypeMappings() {
325    return customDbTypeMappings;
326  }
327
328  public void loadSettings(PropertiesWrapper p) {
329
330    idType = p.getEnum(IdType.class, "idType", idType);
331    forUpdateNoKey = p.getBoolean("forUpdateNoKey", forUpdateNoKey);
332    databaseSequenceBatchSize = p.getInt("databaseSequenceBatchSize", databaseSequenceBatchSize);
333    databaseBooleanTrue = p.get("databaseBooleanTrue", databaseBooleanTrue);
334    databaseBooleanFalse = p.get("databaseBooleanFalse", databaseBooleanFalse);
335    databaseInetAddressVarchar = p.getBoolean("databaseInetAddressVarchar", databaseInetAddressVarchar);
336    caseSensitiveCollation = p.getBoolean("caseSensitiveCollation", caseSensitiveCollation);
337    useMigrationStoredProcedures = p.getBoolean("useMigrationStoredProcedures", useMigrationStoredProcedures);
338
339    DbUuid dbUuid = p.getEnum(DbUuid.class, "dbuuid", null);
340    if (dbUuid != null) {
341      setDbUuid(dbUuid);
342    }
343    if (p.getBoolean("uuidStoreAsBinary", false)) {
344      setDbUuid(DbUuid.BINARY);
345    }
346
347    int srid = p.getInt("geometrySRID", 0);
348    if (srid > 0) {
349      setGeometrySRID(srid);
350    }
351
352    // Mapping is specified in the form: BOOLEAN=int(1);BIT=int(1);
353    String mapping = p.get("mapping");
354    if (mapping != null && !mapping.isEmpty()) {
355      Map<String, String> map = StringHelper.delimitedToMap(mapping, ";", "=");
356      for (Entry<String, String> entry : map.entrySet()) {
357        addCustomMapping(DbType.valueOf(entry.getKey()), entry.getValue());
358      }
359    }
360
361    boolean quotedIdentifiers = p.getBoolean("allQuotedIdentifiers", allQuotedIdentifiers);
362    if (quotedIdentifiers != allQuotedIdentifiers) {
363      // potentially also set to use matching naming convention
364      setAllQuotedIdentifiers(quotedIdentifiers);
365    }
366  }
367
368  /**
369   * Specify how UUID is stored.
370   */
371  public enum DbUuid {
372
373    /**
374     * Store using native UUID in H2 and Postgres and otherwise fallback to VARCHAR(40).
375     */
376    AUTO_VARCHAR(true, false, false),
377
378    /**
379     * Store using native UUID in H2 and Postgres and otherwise fallback to BINARY(16).
380     */
381    AUTO_BINARY(true, true, false),
382
383    /**
384     * Store using native UUID in H2 and Postgres and otherwise fallback to BINARY(16) with optimized packing.
385     */
386    AUTO_BINARY_OPTIMIZED(true, true, true),
387
388    /**
389     * Store using DB VARCHAR(40).
390     */
391    VARCHAR(false, false, false),
392
393    /**
394     * Store using DB BINARY(16).
395     */
396    BINARY(false, true, false),
397
398    /**
399     * Store using DB BINARY(16) with optimized packing.
400     */
401    BINARY_OPTIMIZED(false, true, true);
402
403    boolean nativeType;
404    boolean binary;
405    boolean binaryOptimized;
406
407    DbUuid(boolean nativeType, boolean binary, boolean binaryOptimized) {
408      this.nativeType = nativeType;
409      this.binary = binary;
410      this.binaryOptimized = binaryOptimized;
411    }
412
413    /**
414     * Return true if native UUID type is preferred.
415     */
416    public boolean useNativeType() {
417      return nativeType;
418    }
419
420    /**
421     * Return true if BINARY(16) storage is preferred over VARCHAR(40).
422     */
423    public boolean useBinary() {
424      return binary;
425    }
426
427    /**
428     * Return true, if optimized packing should be used.
429     */
430    public boolean useBinaryOptimized() {
431      return binaryOptimized;
432    }
433  }
434}