#1920 Support @Index concurrently and definition

This commit is contained in:
rob bygrave
2020-01-29 20:22:32 +13:00
parent 680a11a532
commit a894924e96
34 changed files with 401 additions and 114 deletions
+9
View File
@@ -50,4 +50,13 @@ public interface ScriptRunner {
*/
void run(URL resource, Map<String, String> placeholderMap);
/**
* Run the raw provided DDL or SQL script.
*
* @param name The name of the script for logging purposes
* @param content The SQL content
* @param useAutoCommit Set to true to use auto commit true and continue when any errors occur
*/
void runScript(String name, String content, boolean useAutoCommit);
}
@@ -182,7 +182,7 @@ public class DdlGenerator {
}
private DdlRunner createDdlRunner(boolean expectErrors, String scriptName) {
return new DdlRunner(expectErrors, scriptName, DdlAutoCommit.forPlatform(platformName));
return new DdlRunner(expectErrors, scriptName, platformName);
}
protected void runDropSql(Connection connection) throws IOException {
@@ -451,7 +451,7 @@ public class BaseTableDdl implements TableDdl {
String tableName = lowerTableName(request.table());
if (request.indexName() != null) {
// no matching unique constraint so add the index
fkeyBuffer.appendStatement(platformDdl.createIndex(request.indexName(), tableName, request.cols(), false));
fkeyBuffer.appendStatement(platformDdl.createIndex(new WriteCreateIndex(request.indexName(), tableName, request.cols(), false)));
}
alterTableAddForeignKey(write.getOptions(), fkeyBuffer, request);
@@ -611,15 +611,15 @@ public class BaseTableDdl implements TableDdl {
@Override
public void generate(DdlWrite writer, CreateIndex index) throws IOException {
if (platformInclude(index.getPlatforms())) {
writer.apply().appendStatement(platformDdl.createIndex(index.getIndexName(), index.getTableName(), split(index.getColumns()), Boolean.TRUE.equals(index.isUnique())));
writer.dropAll().appendStatement(platformDdl.dropIndex(index.getIndexName(), index.getTableName()));
writer.apply().appendStatement(platformDdl.createIndex(new WriteCreateIndex(index)));
writer.dropAll().appendStatement(platformDdl.dropIndex(index.getIndexName(), index.getTableName(), Boolean.TRUE.equals(index.isConcurrent())));
}
}
@Override
public void generate(DdlWrite writer, DropIndex dropIndex) throws IOException {
if (platformInclude(dropIndex.getPlatforms())) {
writer.apply().appendStatement(platformDdl.dropIndex(dropIndex.getIndexName(), dropIndex.getTableName()));
writer.apply().appendStatement(platformDdl.dropIndex(dropIndex.getIndexName(), dropIndex.getTableName(), Boolean.TRUE.equals(dropIndex.isConcurrent())));
}
}
@@ -56,12 +56,12 @@ public class ClickHouseDdl extends PlatformDdl {
}
@Override
public String dropIndex(String indexName, String tableName) {
public String dropIndex(String indexName, String tableName, boolean concurrent) {
return null;
}
@Override
public String createIndex(String indexName, String tableName, String[] columns, boolean unique) {
public String createIndex(WriteCreateIndex create) {
return null;
}
@@ -13,25 +13,24 @@ public class HanaColumnStoreDdl extends AbstractHanaDdl {
}
@Override
public String createIndex(String indexName, String tableName, String[] columns, boolean unique) {
public String createIndex(WriteCreateIndex create) {
final String[] columns = create.getColumns();
if (columns == null || columns.length == 0) {
return "-- cannot create index: no columns given";
}
if (columns.length == 1) {
return "-- explicit index \"" + indexName + "\" for single column \"" + columns[0] + "\" of table \"" + tableName
return "-- explicit index \"" + create.getIndexName() + "\" for single column \"" + columns[0] + "\" of table \"" + create.getTableName()
+ "\" is not necessary";
}
StringBuilder buffer = new StringBuilder();
buffer.append("create inverted hash index ").append(maxConstraintName(indexName)).append(" on ").append(tableName);
buffer.append("create inverted hash index ").append(maxConstraintName(create.getIndexName())).append(" on ").append(create.getTableName());
appendColumns(columns, buffer);
return buffer.toString();
}
@Override
public String dropIndex(String indexName, String tableName) {
public String dropIndex(String indexName, String tableName, boolean concurrent) {
DdlBuffer buffer = new BaseDdlBuffer(null);
try {
buffer.append("delimiter $$").newLine();
@@ -30,7 +30,7 @@ public class MySqlDdl extends PlatformDdl {
* Return the drop index statement.
*/
@Override
public String dropIndex(String indexName, String tableName) {
public String dropIndex(String indexName, String tableName, boolean concurrent) {
return "drop index " + maxConstraintName(indexName) + " on " + tableName;
}
@@ -110,6 +110,7 @@ public class PlatformDdl {
protected String addForeignKeySkipCheck = "";
protected String uniqueIndex = "unique";
protected String indexConcurrent = "";
/**
* Set false for MsSqlServer to allow multiple nulls for OneToOne mapping.
@@ -382,23 +383,34 @@ public class PlatformDdl {
}
/**
* Return the drop index statement.
* Return the drop index statement for known non concurrent index.
*/
public String dropIndex(String indexName, String tableName) {
return dropIndexIfExists + maxConstraintName(indexName);
return dropIndex(indexName, tableName, false);
}
/**
* Return the create index statement.
* Return the drop index statement.
*/
public String createIndex(String indexName, String tableName, String[] columns, boolean unique) {
public String dropIndex(String indexName, String tableName, boolean concurrent) {
return dropIndexIfExists + maxConstraintName(indexName);
}
public String createIndex(WriteCreateIndex create) {
if (create.useDefinition()) {
return create.getDefinition();
}
StringBuilder buffer = new StringBuilder();
buffer.append("create ");
if (unique) {
if (create.isUnique()) {
buffer.append(uniqueIndex).append(" ");
}
buffer.append("index ").append(maxConstraintName(indexName)).append(" on ").append(tableName);
appendColumns(columns, buffer);
buffer.append("index ");
if (create.isConcurrent()) {
buffer.append(indexConcurrent);
}
buffer.append(maxConstraintName(create.getIndexName())).append(" on ").append(create.getTableName());
appendColumns(create.getColumns(), buffer);
return buffer.toString();
}
@@ -10,6 +10,8 @@ import java.io.IOException;
*/
public class PostgresDdl extends PlatformDdl {
private static final String dropIndexConcurrentlyIfExists = "drop index concurrently if exists ";
public PostgresDdl(DatabasePlatform platform) {
super(platform);
this.historyDdl = new PostgresHistoryDdl();
@@ -18,6 +20,7 @@ public class PostgresDdl extends PlatformDdl {
this.alterTableIfExists = "if exists ";
this.columnSetNull = "drop not null";
this.addForeignKeySkipCheck = " not valid";
this.indexConcurrent = "concurrently ";
}
public String setLockTimeout(int lockTimeoutSeconds) {
@@ -39,7 +42,6 @@ public class PostgresDdl extends PlatformDdl {
*/
@Override
public String asIdentityColumn(String columnDefn) {
if ("bigint".equalsIgnoreCase(columnDefn)) {
return "bigserial";
}
@@ -56,4 +58,9 @@ public class PostgresDdl extends PlatformDdl {
public void addTablePartition(DdlBuffer apply, String partitionMode, String partitionColumn) throws IOException {
apply.append(" partition by range (").append(partitionColumn).append(")");
}
@Override
public String dropIndex(String indexName, String tableName, boolean concurrent) {
return (concurrent ? dropIndexConcurrentlyIfExists : dropIndexIfExists) + maxConstraintName(indexName);
}
}
@@ -58,7 +58,7 @@ public class SqlServerDdl extends PlatformDdl {
}
@Override
public String dropIndex(String indexName, String tableName) {
public String dropIndex(String indexName, String tableName, boolean concurrent) {
return "IF EXISTS (SELECT name FROM sys.indexes WHERE object_id = OBJECT_ID('" + tableName + "','U') AND name = '"
+ maxConstraintName(indexName) + "') drop index " + maxConstraintName(indexName) + " ON " + tableName;
}
@@ -0,0 +1,61 @@
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
import io.ebeaninternal.dbmigration.migration.CreateIndex;
import static io.ebeaninternal.dbmigration.ddlgeneration.platform.SplitColumns.split;
class WriteCreateIndex {
private final String indexName;
private final String tableName;
private final String[] columns;
private final boolean unique;
private final boolean concurrent;
private final String definition;
WriteCreateIndex(String indexName, String tableName, String[] columns, boolean unique) {
this.indexName = indexName;
this.tableName = tableName;
this.columns = columns;
this.unique = unique;
this.concurrent = false;
this.definition = null;
}
public WriteCreateIndex(CreateIndex index) {
this.indexName = index.getIndexName();
this.tableName = index.getTableName();
this.columns = split(index.getColumns());
this.unique = Boolean.TRUE.equals(index.isUnique());
this.concurrent = Boolean.TRUE.equals(index.isConcurrent());
this.definition = index.getDefinition();
}
public String getIndexName() {
return indexName;
}
public String getTableName() {
return tableName;
}
public String[] getColumns() {
return columns;
}
public boolean isUnique() {
return unique;
}
public boolean isConcurrent() {
return concurrent;
}
public String getDefinition() {
return definition;
}
public boolean useDefinition() {
return definition != null && !definition.isEmpty();
}
}
@@ -146,16 +146,23 @@ public class AddUniqueConstraint {
}
/**
* Return the platforms.
* Gets the value of the platforms property.
*
* @return possible object is
* {@link String }
*/
public String getPlatforms() {
return platforms;
}
/**
* Set the platforms.
* Sets the value of the platforms property.
*
* @param value allowed object is
* {@link String }
*/
public void setPlatforms(String platforms) {
this.platforms = platforms;
public void setPlatforms(String value) {
this.platforms = value;
}
}
@@ -20,6 +20,8 @@ import javax.xml.bind.annotation.XmlType;
* &lt;attribute name="tableName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="columns" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="unique" type="{http://www.w3.org/2001/XMLSchema}boolean" />
* &lt;attribute name="concurrent" type="{http://www.w3.org/2001/XMLSchema}boolean" />
* &lt;attribute name="definition" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="platforms" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;/restriction>
* &lt;/complexContent>
@@ -39,6 +41,10 @@ public class CreateIndex {
protected String columns;
@XmlAttribute(name = "unique")
protected Boolean unique;
@XmlAttribute(name = "concurrent")
protected Boolean concurrent;
@XmlAttribute(name = "definition")
protected String definition;
@XmlAttribute(name = "platforms")
protected String platforms;
@@ -103,30 +109,83 @@ public class CreateIndex {
}
/**
* Return the unique property.
* Gets the value of the unique property.
*
* @return possible object is
* {@link Boolean }
*/
public Boolean isUnique() {
return unique;
}
/**
* Set the unique property.
* Sets the value of the unique property.
*
* @param value allowed object is
* {@link Boolean }
*/
public void setUnique(Boolean unique) {
this.unique = unique;
public void setUnique(Boolean value) {
this.unique = value;
}
/**
* Return the platforms.
* Gets the value of the concurrent property.
*
* @return possible object is
* {@link Boolean }
*/
public Boolean isConcurrent() {
return concurrent;
}
/**
* Sets the value of the concurrent property.
*
* @param value allowed object is
* {@link Boolean }
*/
public void setConcurrent(Boolean value) {
this.concurrent = value;
}
/**
* Gets the value of the definition property.
*
* @return possible object is
* {@link String }
*/
public String getDefinition() {
return definition;
}
/**
* Sets the value of the definition property.
*
* @param value allowed object is
* {@link String }
*/
public void setDefinition(String value) {
this.definition = value;
}
/**
* Gets the value of the platforms property.
*
* @return possible object is
* {@link String }
*/
public String getPlatforms() {
return platforms;
}
/**
* Set the platforms.
* Sets the value of the platforms property.
*
* @param value allowed object is
* {@link String }
*/
public void setPlatforms(String platforms) {
this.platforms = platforms;
public void setPlatforms(String value) {
this.platforms = value;
}
}
@@ -37,6 +37,7 @@ import java.util.List;
* &lt;attribute name="sequenceInitial" type="{http://www.w3.org/2001/XMLSchema}positiveInteger" />
* &lt;attribute name="sequenceAllocate" type="{http://www.w3.org/2001/XMLSchema}positiveInteger" />
* &lt;attribute name="pkName" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="storageEngine" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;/restriction>
* &lt;/complexContent>
* &lt;/complexType>
@@ -18,6 +18,7 @@ import javax.xml.bind.annotation.XmlType;
* &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
* &lt;attribute name="indexName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="tableName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="concurrent" type="{http://www.w3.org/2001/XMLSchema}boolean" />
* &lt;attribute name="platforms" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;/restriction>
* &lt;/complexContent>
@@ -33,6 +34,8 @@ public class DropIndex {
protected String indexName;
@XmlAttribute(name = "tableName", required = true)
protected String tableName;
@XmlAttribute(name = "concurrent")
protected Boolean concurrent;
@XmlAttribute(name = "platforms")
protected String platforms;
@@ -77,16 +80,43 @@ public class DropIndex {
}
/**
* Return the platforms.
* Gets the value of the concurrent property.
*
* @return possible object is
* {@link Boolean }
*/
public Boolean isConcurrent() {
return concurrent;
}
/**
* Sets the value of the concurrent property.
*
* @param value allowed object is
* {@link Boolean }
*/
public void setConcurrent(Boolean value) {
this.concurrent = value;
}
/**
* Gets the value of the platforms property.
*
* @return possible object is
* {@link String }
*/
public String getPlatforms() {
return platforms;
}
/**
* Set the platforms.
* Sets the value of the platforms property.
*
* @param value allowed object is
* {@link String }
*/
public void setPlatforms(String platforms) {
this.platforms = platforms;
public void setPlatforms(String value) {
this.platforms = value;
}
}
@@ -123,16 +123,23 @@ public class UniqueConstraint {
}
/**
* Return the platforms.
* Gets the value of the platforms property.
*
* @return possible object is
* {@link String }
*/
public String getPlatforms() {
return platforms;
}
/**
* Set the platforms.
* Sets the value of the platforms property.
*
* @param value allowed object is
* {@link String }
*/
public void setPlatforms(String platforms) {
this.platforms = platforms;
public void setPlatforms(String value) {
this.platforms = value;
}
}
@@ -6,6 +6,7 @@ import io.ebeaninternal.dbmigration.migration.DropIndex;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
/**
* Index as part of the logical model.
@@ -13,14 +14,12 @@ import java.util.List;
public class MIndex {
private String tableName;
private String indexName;
private String platforms;
private List<String> columns = new ArrayList<>();
private boolean unique;
private boolean concurrent;
private String definition;
/**
* Create a single column non unique index.
@@ -31,12 +30,6 @@ public class MIndex {
this.columns.add(columnName);
}
public MIndex(String indexName, String tableName, String[] columnNames, String platforms, boolean unique) {
this(indexName, tableName, columnNames);
this.platforms = platforms;
this.unique = unique;
}
/**
* Create a multi column non unique index.
*/
@@ -46,12 +39,22 @@ public class MIndex {
Collections.addAll(this.columns, columnNames);
}
public MIndex(String indexName, String tableName, String[] columnNames, String platforms, boolean unique, boolean concurrent, String definition) {
this(indexName, tableName, columnNames);
this.platforms = platforms;
this.unique = unique;
this.concurrent = concurrent;
this.definition = emptyToNull(definition);
}
public MIndex(CreateIndex createIndex) {
this.indexName = createIndex.getIndexName();
this.tableName = createIndex.getTableName();
this.columns = split(createIndex.getColumns());
this.platforms = createIndex.getPlatforms();
this.unique = Boolean.TRUE.equals(createIndex.isUnique());
this.concurrent = Boolean.TRUE.equals(createIndex.isConcurrent());
this.definition = emptyToNull(createIndex.getDefinition());
}
public String getKey() {
@@ -92,9 +95,20 @@ public class MIndex {
if (Boolean.TRUE.equals(unique)) {
create.setUnique(Boolean.TRUE);
}
if (Boolean.TRUE.equals(concurrent)) {
create.setConcurrent(Boolean.TRUE);
}
create.setDefinition(emptyToNull(definition));
return create;
}
private String emptyToNull(String val) {
if (val == null || val.isEmpty()) {
return null;
}
return val;
}
/**
* Create a DropIndex migration for this index.
*/
@@ -103,6 +117,9 @@ public class MIndex {
dropIndex.setIndexName(indexName);
dropIndex.setTableName(tableName);
dropIndex.setPlatforms(platforms);
if (Boolean.TRUE.equals(concurrent)) {
dropIndex.setConcurrent(Boolean.TRUE);
}
return dropIndex;
}
@@ -127,6 +144,9 @@ public class MIndex {
if (unique != newIndex.unique) {
return true;
}
if (!Objects.equals(definition, newIndex.definition)) {
return true;
}
List<String> newColumns = newIndex.getColumns();
if (columns.size() != newColumns.size()) {
return true;
@@ -141,6 +161,9 @@ public class MIndex {
private List<String> split(String columns) {
if (columns.isEmpty()) {
return Collections.emptyList();
}
String[] cols = columns.split(",");
List<String> colList = new ArrayList<>(cols.length);
Collections.addAll(colList, cols);
@@ -92,7 +92,7 @@ public class ModelBuildPropertyVisitor extends BaseTablePropertyVisitor {
}
private MIndex createMIndex(String indexName, String tableName, IndexDefinition index) {
return new MIndex(indexName, tableName, index.getColumns(), platforms(index.getPlatforms()), index.isUnique());
return new MIndex(indexName, tableName, index.getColumns(), platforms(index.getPlatforms()), index.isUnique(), index.isConcurrent(), index.getDefinition());
}
private String platforms(Platform[] platforms) {
@@ -22,9 +22,11 @@ class DScriptRunner implements ScriptRunner {
private static final String NEWLINE = "\n";
private final SpiEbeanServer server;
private final String platformName;
DScriptRunner(SpiEbeanServer server) {
this.server = server;
this.platformName = this.server.getDatabasePlatform().getPlatform().base().name();
}
@Override
@@ -56,7 +58,7 @@ class DScriptRunner implements ScriptRunner {
}
String content = content(resource);
runScript(content, scriptName, placeholderMap);
runScript(content, scriptName, placeholderMap, false);
}
private String content(URL resource) {
@@ -72,21 +74,25 @@ class DScriptRunner implements ScriptRunner {
}
}
@Override
public void runScript(String name, String content, boolean useAutoCommit) {
runScript(content, name, null, useAutoCommit);
}
/**
* Execute all the DDL statements in the script.
*/
private void runScript(String content, String scriptName, Map<String, String> placeholderMap) {
private void runScript(String content, String scriptName, Map<String, String> placeholderMap, boolean useAutoCommit) {
try {
if (placeholderMap != null) {
content = ScriptTransform.build(null, placeholderMap).transform(content);
}
try (Connection connection = obtainConnection()) {
DdlRunner runner = new DdlRunner(false, scriptName);
DdlRunner runner = new DdlRunner(useAutoCommit, scriptName, platformName);
runner.runAll(content, connection);
connection.commit();
runner.runNonTransactional(connection);
}
} catch (SQLException e) {
@@ -8,21 +8,22 @@ import io.ebean.annotation.Platform;
public class IndexDefinition {
private final String[] columns;
private final String name;
private final Platform[] platforms;
private final boolean unique;
private final boolean concurrent;
private final String definition;
/**
* Create from Index annotation.
*/
public IndexDefinition(String[] columns, String name, boolean unique, Platform[] platforms) {
public IndexDefinition(String[] columns, String name, boolean unique, Platform[] platforms, boolean concurrent, String definition) {
this.columns = columns;
this.unique = unique;
this.name = name;
this.platforms = platforms;
this.concurrent = concurrent;
this.definition = definition;
}
/**
@@ -33,13 +34,19 @@ public class IndexDefinition {
this.unique = true;
this.name = null;
this.platforms = null;
this.concurrent = false;
this.definition = null;
}
/**
* Return true if this can be used as a unique constraint.
*/
public boolean isUniqueConstraint() {
return unique && noColumnFormulas();
return unique && !concurrent && noDefinition() && noColumnFormulas();
}
private boolean noDefinition() {
return definition == null || definition.isEmpty();
}
private boolean noColumnFormulas() {
@@ -78,4 +85,18 @@ public class IndexDefinition {
public Platform[] getPlatforms() {
return platforms;
}
/**
* Return true if this index has the concurrent flag.
*/
public boolean isConcurrent() {
return concurrent;
}
/**
* Return the raw definition of the index if supplied.
*/
public String getDefinition() {
return definition;
}
}
@@ -134,7 +134,8 @@ public class AnnotationClass extends AnnotationParser {
}
for (Index index : findAnnotationsRecursive(cls, Index.class)) {
descriptor.addIndex(new IndexDefinition(convertColumnNames(index.columnNames()), index.name(), index.unique(), index.platforms()));
descriptor.addIndex(new IndexDefinition(convertColumnNames(index.columnNames()), index.name(),
index.unique(), index.platforms(), index.concurrent(), index.definition()));
}
UniqueConstraint uc = findAnnotationRecursive(cls, UniqueConstraint.class);
@@ -416,7 +416,7 @@ public class AnnotationFields extends AnnotationParser {
if (columnNames.length == 1 && hasRelationshipItem(prop)) {
throw new RuntimeException("Can't use Index on foreign key relationships.");
}
descriptor.addIndex(new IndexDefinition(columnNames, index.name(), index.unique(), index.platforms()));
descriptor.addIndex(new IndexDefinition(columnNames, index.name(), index.unique(), index.platforms(), index.concurrent(), index.definition()));
}
private void readJsonAnnotations(DeployBeanProperty prop) {