package io.ebean.dbmigration.model.build;
import io.ebean.config.DbConstraintNaming;
import io.ebean.config.dbplatform.DbPlatformType;
import io.ebean.config.dbplatform.DbPlatformTypeMapping;
import io.ebean.dbmigration.model.MColumn;
import io.ebean.dbmigration.model.MTable;
import io.ebean.dbmigration.model.ModelContainer;
import io.ebeaninternal.server.deploy.BeanProperty;
import io.ebeaninternal.server.type.ScalarType;
import java.util.Collection;
import java.util.List;
/**
* The context used during DDL generation.
*/
public class ModelBuildContext {
/**
* Use platform agnostic logical types. These types are converted to
* platform specific types in the DDL generation.
*/
private final DbPlatformTypeMapping dbTypeMap = DbPlatformTypeMapping.logicalTypes();
private final ModelContainer model;
private final DbConstraintNaming constraintNaming;
private final DbConstraintNaming.MaxLength maxLength;
private final boolean platformTypes;
public ModelBuildContext(ModelContainer model, DbConstraintNaming naming, DbConstraintNaming.MaxLength maxLength, boolean platformTypes) {
this.model = model;
this.constraintNaming = naming;
this.maxLength = maxLength;
this.platformTypes = platformTypes;
}
/**
* Adjust the foreign key references on any draft tables (that reference other draft tables).
* This is called as a 'second pass' after all the draft tables have been identified.
*/
public void adjustDraftReferences() {
model.adjustDraftReferences();
}
public String normaliseTable(String baseTable) {
return constraintNaming.normaliseTable(baseTable);
}
public String primaryKeyName(String tableName) {
return maxLength(constraintNaming.primaryKeyName(tableName), 0);
}
public String foreignKeyConstraintName(String tableName, String columnName, int foreignKeyCount) {
return maxLength(constraintNaming.foreignKeyConstraintName(tableName, columnName), foreignKeyCount);
}
public String foreignKeyIndexName(String tableName, String[] columns, int indexCount) {
return maxLength(constraintNaming.foreignKeyIndexName(tableName, columns), indexCount);
}
public String foreignKeyIndexName(String tableName, String column, int indexCount) {
return maxLength(constraintNaming.foreignKeyIndexName(tableName, column), indexCount);
}
public String indexName(String tableName, String column, int indexCount) {
return maxLength(constraintNaming.indexName(tableName, column), indexCount);
}
public String indexName(String tableName, String[] columns, int indexCount) {
return maxLength(constraintNaming.indexName(tableName, columns), indexCount);
}
public String uniqueConstraintName(String tableName, String columnName, int indexCount) {
return maxLength(constraintNaming.uniqueConstraintName(tableName, columnName), indexCount);
}
public String uniqueConstraintName(String tableName, String[] columnNames, int indexCount) {
return maxLength(constraintNaming.uniqueConstraintName(tableName, columnNames), indexCount);
}
public String checkConstraintName(String tableName, String columnName, int checkCount) {
return maxLength(constraintNaming.checkConstraintName(tableName, columnName), checkCount);
}
public MTable addTable(MTable table) {
return model.addTable(table);
}
public void addIndex(String indexName, String tableName, String columnName) {
model.addIndex(indexName, tableName, columnName);
}
public void addIndex(String indexName, String tableName, String[] columnNames) {
model.addIndex(indexName, tableName, columnNames);
}
private String maxLength(String constraintName, int indexCount) {
return maxLength.maxLength(constraintName, indexCount);
}
/**
* Return the map used to determine the DB specific type
* for a given bean property.
*/
public DbPlatformTypeMapping getDbTypeMap() {
return dbTypeMap;
}
/**
* Render the DB type for this property given the strict mode.
*/
public String getColumnDefn(BeanProperty p, boolean strict) {
DbPlatformType dbType = getDbType(p);
if (dbType == null) {
throw new IllegalStateException("Unknown DbType mapping for " + p.getFullBeanName());
}
return p.renderDbType(dbType, strict);
}
private DbPlatformType getDbType(BeanProperty p) {
if (p.isDbEncrypted()) {
return dbTypeMap.get(p.getDbEncryptedType());
}
if (p.isLocalEncrypted()) {
// scalar type potentially wrapping varbinary db type
ScalarType<Object> scalarType = p.getScalarType();
int jdbcType = scalarType.getJdbcType();
return dbTypeMap.get(jdbcType);
}
// can be the logical JSON types (JSON, JSONB, JSONClob, JSONBlob, JSONVarchar)
int dbType = p.getDbType(platformTypes);
if (dbType == 0) {
throw new RuntimeException("No scalarType defined for " + p.getFullBeanName());
}
return dbTypeMap.get(dbType);
}
/**
* Create the draft table for a given table.
*/
public void createDraft(MTable table, boolean draftable) {
MTable draftTable = table.createDraftTable();
draftTable.setPkName(primaryKeyName(draftTable.getName()));
if (draftable) {
// Add a FK from @Draftable live table back to it's draft table)
List<MColumn> pkCols = table.primaryKeyColumns();
if (pkCols.size() == 1) {
// only doing this for single column PK at this stage
MColumn pk = pkCols.get(0);
pk.setReferences(draftTable.getName() + "." + pk.getName());
pk.setForeignKeyName(foreignKeyConstraintName(table.getName(), pk.getName(), 0));
}
}
int fkCount = 0;
int ixCount = 0;
int uqCount = 0;
Collection<MColumn> cols = draftTable.allColumns();
for (MColumn col : cols) {
if (col.getForeignKeyName() != null) {
// Note that we adjust the 'references' table later in a second pass
// after we know all the tables that are 'draftable'
//col.setReferences(refTable + "." + refColumn);
col.setForeignKeyName(foreignKeyConstraintName(draftTable.getName(), col.getName(), ++fkCount));
String[] indexCols = {col.getName()};
col.setForeignKeyIndex(foreignKeyIndexName(draftTable.getName(), indexCols, ++ixCount));
}
// adjust the unique constraint names
if (col.getUnique() != null) {
col.setUnique(uniqueConstraintName(draftTable.getName(), col.getName(), ++uqCount));
}
if (col.getUniqueOneToOne() != null) {
col.setUniqueOneToOne(uniqueConstraintName(draftTable.getName(), col.getName(), ++uqCount));
}
}
addTable(draftTable);
}
}