001package com.avaje.ebean.config.dbplatform;
002
003import com.avaje.ebean.config.ServerConfig;
004
005import java.sql.Types;
006import java.util.HashMap;
007import java.util.Map;
008
009/**
010 * Used to map bean property types to DB specific types for DDL generation.
011 */
012public class DbTypeMap {
013
014  private static final DbType UUID_NATIVE = new DbType("uuid", false);
015  private static final DbType UUID_PLACEHOLDER = new DbType("uuidPlaceholder");
016  private static final DbType JSON_CLOB_PLACEHOLDER = new DbType("jsonClobPlaceholder");
017  private static final DbType JSON_BLOB_PLACEHOLDER = new DbType("jsonBlobPlaceholder");
018  private static final DbType JSON_VARCHAR_PLACEHOLDER = new DbType("jsonVarcharPlaceholder");
019
020  /**
021   * A map to reverse lookup the type by name.
022   * <p>
023   * Used when converting from logical types to platform types which we
024   * want to do with 2 phase DDL generation.
025   */
026  static Map<String, Integer> lookup = new HashMap<String, Integer>();
027
028  static {
029    lookup.put("BOOLEAN", Types.BOOLEAN);
030    lookup.put("BIT", Types.BIT);
031    lookup.put("INTEGER", Types.INTEGER);
032    lookup.put("BIGINT", Types.BIGINT);
033    lookup.put("REAL", Types.REAL);
034    // Float is most common REAL mapping to have that as well
035    lookup.put("FLOAT", Types.REAL);
036
037    lookup.put("DOUBLE", Types.DOUBLE);
038    lookup.put("SMALLINT", Types.SMALLINT);
039    lookup.put("TINYINT", Types.TINYINT);
040    lookup.put("DECIMAL", Types.DECIMAL);
041    lookup.put("VARCHAR", Types.VARCHAR);
042    // VARCHAR2 - extra for Oracle specific column definition
043    lookup.put("VARCHAR2", Types.VARCHAR);
044    lookup.put("CHAR", Types.CHAR);
045    lookup.put("BLOB", Types.BLOB);
046    lookup.put("CLOB", Types.CLOB);
047
048    lookup.put("LONGVARBINARY", Types.LONGVARBINARY);
049    lookup.put("LONGVARCHAR", Types.LONGVARCHAR);
050    lookup.put("VARBINARY", Types.VARBINARY);
051    lookup.put("BINARY", Types.BINARY);
052    lookup.put("DATE", Types.DATE);
053    lookup.put("TIME", Types.TIME);
054    lookup.put("TIMESTAMP", Types.TIMESTAMP);
055
056    lookup.put("ARRAY", Types.ARRAY);
057    lookup.put("UUID", DbType.UUID);
058
059    // Not standard java.sql.Types
060    // logical JSON storage types
061    lookup.put("JSON", DbType.JSON);
062    lookup.put("JSONB", DbType.JSONB);
063    lookup.put("JSONCLOB", DbType.JSONClob);
064    lookup.put("JSONBLOB", DbType.JSONBlob);
065    lookup.put("JSONVARCHAR", DbType.JSONVarchar);
066  }
067
068
069  private final Map<Integer, DbType> typeMap = new HashMap<Integer, DbType>();
070
071  /**
072   * Return the DbTypeMap with standard (not platform specific) types.
073   * <p>
074   * This has some extended JSON types (JSON, JSONB, JSONVarchar, JSONClob, JSONBlob).
075   * These types get translated to specific database platform types during DDL generation.
076   */
077  public static DbTypeMap logicalTypes() {
078    return new DbTypeMap(true);
079  }
080
081  public DbTypeMap() {
082    loadDefaults(false);
083  }
084
085  private DbTypeMap(boolean logicalTypes) {
086    loadDefaults(logicalTypes);
087  }
088
089  /**
090   * Load the standard types. These can be overridden by DB specific platform.
091   */
092  private void loadDefaults(boolean logicalTypes) {
093
094    put(Types.BOOLEAN, new DbType("boolean"));
095    put(Types.BIT, new DbType("bit"));
096
097    put(Types.INTEGER, new DbType("integer"));
098    put(Types.BIGINT, new DbType("bigint"));
099    put(Types.REAL, new DbType("float"));
100    put(Types.DOUBLE, new DbType("double"));
101    put(Types.SMALLINT, new DbType("smallint"));
102    put(Types.TINYINT, new DbType("tinyint"));
103    put(Types.DECIMAL, new DbType("decimal", 38));
104
105    put(Types.VARCHAR, new DbType("varchar", 255));
106    put(Types.CHAR, new DbType("char", 1));
107
108    put(Types.BLOB, new DbType("blob"));
109    put(Types.CLOB, new DbType("clob"));
110
111    put(Types.ARRAY, new DbType("array"));
112
113    if (logicalTypes) {
114      // keep it logical for 2 layer DDL generation
115      put(DbType.HSTORE, new DbType("hstore", false));
116      put(DbType.JSON, new DbType("json", false));
117      put(DbType.JSONB, new DbType("jsonb", false));
118      put(DbType.JSONClob, new DbType("jsonclob"));
119      put(DbType.JSONBlob, new DbType("jsonblob"));
120      put(DbType.JSONVarchar, new DbType("jsonvarchar", 1000));
121      put(DbType.UUID, UUID_NATIVE);
122
123    } else {
124      put(DbType.JSON, JSON_CLOB_PLACEHOLDER); // Postgres maps this to JSON
125      put(DbType.JSONB, JSON_CLOB_PLACEHOLDER); // Postgres maps this to JSONB
126      put(DbType.JSONClob, JSON_CLOB_PLACEHOLDER);
127      put(DbType.JSONBlob, JSON_BLOB_PLACEHOLDER);
128      put(DbType.JSONVarchar, JSON_VARCHAR_PLACEHOLDER);
129      put(DbType.UUID, UUID_PLACEHOLDER);
130    }
131
132    put(Types.LONGVARBINARY, new DbType("longvarbinary"));
133    put(Types.LONGVARCHAR, new DbType("lonvarchar"));
134    put(Types.VARBINARY, new DbType("varbinary", 255));
135    put(Types.BINARY, new DbType("binary", 255));
136
137    put(Types.DATE, new DbType("date"));
138    put(Types.TIME, new DbType("time"));
139    put(Types.TIMESTAMP, new DbType("timestamp"));
140  }
141
142  /**
143   * Lookup the platform specific DbType given the standard sql type name.
144   */
145  public DbType lookup(String name, boolean withScale) {
146    name = name.trim().toUpperCase();
147    Integer typeKey = lookup.get(name);
148    if (typeKey == null) {
149      throw new IllegalArgumentException("Unknown type [" + name + "] - not standard sql type");
150    }
151    // handle JSON types mapped to clob, blob and varchar
152    switch (typeKey) {
153      case DbType.JSONBlob:
154        return get(Types.BLOB);
155      case DbType.JSONClob:
156        return get(Types.CLOB);
157      case DbType.JSONVarchar:
158        return get(Types.VARCHAR);
159      case DbType.JSON:
160        return getJsonType(DbType.JSON, withScale);
161      case DbType.JSONB:
162        return getJsonType(DbType.JSONB, withScale);
163      default:
164        return get(typeKey);
165    }
166  }
167
168  private DbType getJsonType(int type, boolean withScale) {
169    DbType dbType = get(type);
170    if (dbType == JSON_CLOB_PLACEHOLDER) {
171      // if we have scale that implies this maps to varchar
172      return withScale ? get(Types.VARCHAR) : get(Types.CLOB);
173    }
174    if (dbType == JSON_BLOB_PLACEHOLDER) {
175      return get(Types.BLOB);
176    }
177    if (dbType == JSON_VARCHAR_PLACEHOLDER) {
178      return get(Types.VARCHAR);
179    }
180    // Postgres has specific type
181    return get(type);
182  }
183
184  /**
185   * Override the type for a given JDBC type.
186   */
187  public void put(int jdbcType, DbType dbType) {
188    typeMap.put(jdbcType, dbType);
189  }
190
191  /**
192   * Return the type for a given jdbc type.
193   */
194  public DbType get(int jdbcType) {
195    return typeMap.get(jdbcType);
196  }
197
198  /**
199   * Map the UUID appropriately based on native DB support and ServerConfig.DbUuid.
200   */
201  public void config(boolean nativeUuidType, ServerConfig.DbUuid dbUuid) {
202    if (nativeUuidType && dbUuid.useNativeType()) {
203      put(DbType.UUID, UUID_NATIVE);
204    } else if (dbUuid.useBinary()) {
205      put(DbType.UUID, get(Types.BINARY).withLength(16));
206    } else {
207      put(DbType.UUID, get(Types.VARCHAR).withLength(40));
208    }
209  }
210}