Support duplicate index detection, max constraint names - Internal changes for DB Migration / DDL generation #369

This commit is contained in:
Robin Bygrave
2015-08-06 07:39:50 +12:00
parent efd5e16511
commit 9e170b40f6
5 changed files with 177 additions and 23 deletions
@@ -23,11 +23,38 @@ public class BaseTableDdl implements TableDdl {
protected final PlatformDdl platformDdl;
/**
* Used to check that indexes on foreign keys should be skipped as a unique index on the columns
* already exists.
*/
protected IndexSet indexSet = new IndexSet();
// counters used when constraint names are truncated due to maximum length
// and these counters are used to keep the constraint name unique
protected int countCheck;
protected int countUnique;
protected int countForeignKey;
protected int countIndex;
/**
* Construct with a naming convention and platform specific DDL.
*/
public BaseTableDdl(DdlNamingConvention namingConvention, PlatformDdl platformDdl) {
this.namingConvention = namingConvention;
this.platformDdl = platformDdl;
}
/**
* Reset counters and index set for each table.
*/
protected void reset() {
indexSet.clear();
countCheck = 0;
countUnique = 0;
countForeignKey = 0;
countIndex = 0;
}
/**
* Generate the appropriate 'create table' and matching 'drop table' statements
* and add them to the 'apply' and 'rollback' buffers.
@@ -35,6 +62,8 @@ public class BaseTableDdl implements TableDdl {
@Override
public void generate(DdlWrite writer, CreateTable createTable) throws IOException {
reset();
String tableName = lowerName(createTable.getName());
List<Column> columns = createTable.getColumn();
List<Column> pk = determinePrimaryKeyColumns(columns);
@@ -176,15 +205,21 @@ public class BaseTableDdl implements TableDdl {
String indexName = determineForeignKeyIndexName(tableName, columns);
fkeyBuffer.append("create index ").append(indexName).append(" on ").append(tableName);
appendColumns(columns, fkeyBuffer);
fkeyBuffer.endOfStatement();
boolean addIndex = indexSet.add(columns);
if (addIndex) {
// no matching unique constraint so add the index
fkeyBuffer.append("create index ").append(indexName).append(" on ").append(tableName);
appendColumns(columns, fkeyBuffer);
fkeyBuffer.endOfStatement();
}
fkeyBuffer.end();
write.rollbackForeignKeys()
.append("drop index ").append(indexName)
.endOfStatement();
if (addIndex) {
write.rollbackForeignKeys()
.append("drop index ").append(indexName)
.endOfStatement();
}
write.rollbackForeignKeys()
.append("alter table ").append(tableName).append(" drop constraint ").append(fkName)
@@ -253,6 +288,7 @@ public class BaseTableDdl implements TableDdl {
for (Column column : columns) {
if (isTrue(column.isUnique())) {
inlineUniqueConstraintSingle(apply, createTable.getName(), column);
indexSet.add(column);
}
}
}
@@ -369,7 +405,7 @@ public class BaseTableDdl implements TableDdl {
*/
protected String determineForeignKeyConstraintName(String tableName, String columnName) {
return namingConvention.foreignKeyConstraintName(tableName, columnName);
return namingConvention.foreignKeyConstraintName(tableName, columnName, ++countForeignKey);
}
/**
@@ -377,7 +413,7 @@ public class BaseTableDdl implements TableDdl {
*/
protected String determineForeignKeyIndexName(String tableName, String[] columns) {
return namingConvention.foreignKeyIndexName(tableName, columns);
return namingConvention.foreignKeyIndexName(tableName, columns, ++countIndex);
}
/**
@@ -385,7 +421,7 @@ public class BaseTableDdl implements TableDdl {
*/
protected String determineUniqueConstraintName(String tableName, String columnName) {
return namingConvention.uniqueConstraintName(tableName, columnName);
return namingConvention.uniqueConstraintName(tableName, columnName, ++countUnique);
}
/**
@@ -393,7 +429,7 @@ public class BaseTableDdl implements TableDdl {
*/
protected String determineCheckConstraintName(String tableName, String columnName) {
return namingConvention.checkConstraintName(tableName, columnName);
return namingConvention.checkConstraintName(tableName, columnName, ++countCheck);
}
/**
@@ -427,4 +463,82 @@ public class BaseTableDdl implements TableDdl {
return (value == null) ? 0 : value.intValue();
}
/**
* The indexes held on the table.
* <p>
* Used to detect when we don't need to add an index on the foreign key columns
* when there is an existing unique constraint with the same columns.
*/
protected static class IndexSet {
private List<IndexColumns> indexes = new ArrayList<IndexColumns>();
/**
* Clear the indexes (for each table).
*/
public void clear() {
indexes.clear();
}
/**
* Add an index for the given column.
*/
public void add(Column column) {
indexes.add(new IndexColumns(column));
}
/**
* Return true if an index should be added for the given columns.
* <p>
* Returning false indicates there is an existing index (unique constraint) with these columns
* and that an extra index should not be added.
* </p>
*/
public boolean add(String[] columns) {
IndexColumns newIndex = new IndexColumns(columns);
for (int i = 0; i <indexes.size() ; i++) {
if (indexes.get(i).isMatch(newIndex)) {
return false;
}
}
indexes.add(newIndex);
return true;
}
}
/**
* Set of columns making up a particular index (column order is important).
*/
protected static class IndexColumns {
List<String> columns = new ArrayList<String>(4);
/**
* Construct representing as a single column index.
*/
public IndexColumns(Column column) {
columns.add(column.getName());
}
/**
* Construct representing index.
*/
public IndexColumns(String[] columnNames) {
for (int i = 0; i <columnNames.length; i++) {
columns.add(columnNames[i]);
}
}
/**
* Return true if there this index match (same columns same order).
*/
public boolean isMatch(IndexColumns other) {
return columns.equals(other.columns);
}
protected void add(String column) {
columns.add(column);
}
}
}
@@ -22,6 +22,8 @@ public class DdlNamingConvention {
protected String ckPrefix = "ck_";
protected String ckSuffix = "";
protected int maxConstraintNameLength = 32;
protected boolean lowerCaseNames = true;
protected DdlNameNormalise normalise = new DdlNameNormalise();
@@ -34,20 +36,20 @@ public class DdlNamingConvention {
*/
public String primaryKeyName(String tableName) {
return pkPrefix + normaliseTable(tableName) + pkSuffix;
return maxLength(pkPrefix + normaliseTable(tableName) + pkSuffix, 0);
}
/**
* Return the foreign key constraint name given a single column foreign key.
*/
public String foreignKeyConstraintName(String tableName, String columnName) {
return fkPrefix + normaliseTable(tableName) + fkMiddle + normaliseColumn(columnName) + fkSuffix;
public String foreignKeyConstraintName(String tableName, String columnName, int foreignKeyCount) {
return maxLength(fkPrefix + normaliseTable(tableName) + fkMiddle + normaliseColumn(columnName) + fkSuffix, foreignKeyCount);
}
/**
* Return the index name associated with a foreign key constraint given a single column foreign key.
*/
public String foreignKeyIndexName(String tableName, String[] columns) {
public String foreignKeyIndexName(String tableName, String[] columns, int indexCount) {
String colPart;
if (columns.length == 1) {
@@ -62,24 +64,23 @@ public class DdlNamingConvention {
}
colPart = sb.toString();
}
//FIXME: apply max length
return fkIndexPrefix + normaliseTable(tableName) + fkIndexMiddle + colPart + fkIndexSuffix;
return maxLength(fkIndexPrefix + normaliseTable(tableName) + fkIndexMiddle + colPart + fkIndexSuffix, indexCount);
}
/**
* Return the unique constraint name.
*/
public String uniqueConstraintName(String tableName, String columnName) {
public String uniqueConstraintName(String tableName, String columnName, int indexCount) {
return uqPrefix + normaliseTable(tableName) + "_" + normaliseColumn(columnName) + uqSuffix;
return maxLength(uqPrefix + normaliseTable(tableName) + "_" + normaliseColumn(columnName) + uqSuffix, indexCount);
}
/**
* Return the check constraint name.
*/
public String checkConstraintName(String tableName, String columnName) {
public String checkConstraintName(String tableName, String columnName, int checkCount) {
return ckPrefix + normaliseTable(tableName) + "_" + normaliseColumn(columnName) + ckSuffix;
return maxLength(ckPrefix + normaliseTable(tableName) + "_" + normaliseColumn(columnName) + ckSuffix, checkCount);
}
/**
@@ -95,6 +96,18 @@ public class DdlNamingConvention {
return (sequenceName != null) ? lowerName(sequenceName) : normaliseTable(tableName) + "_seq";
}
/**
* Apply a maximum length to the constraint name.
*/
protected String maxLength(String constraintName, int count) {
if (constraintName.length() < maxConstraintNameLength) {
return constraintName;
}
// add the count to ensure the constraint name is unique
// (relying on the prefix having the table name to be globally unique)
return constraintName.substring(0,maxConstraintNameLength-3)+"_"+count;
}
/**
* Normalise the table name by trimming catalog and schema and removing any
* quoted identifier characters (",',[,] etc).
@@ -30,6 +30,8 @@ public class PlatformDdl {
*/
protected String dropTableIfExists = "drop table if exists ";
protected String dropTableCascade = "";
/**
* Default assumes if exists is supported.
*/
@@ -107,7 +109,7 @@ public class PlatformDdl {
}
public String dropTable(String tableName) {
return dropTableIfExists + tableName;
return dropTableIfExists + tableName + dropTableCascade;
}
public String lowerName(String name) {