diff --git a/pom.xml b/pom.xml
index f5e65dff5..bfc7d5897 100644
--- a/pom.xml
+++ b/pom.xml
@@ -130,7 +130,7 @@
io.ebean
ebean-migration
- 11.3.1
+ 11.5.1
diff --git a/src/main/java/io/ebean/config/dbplatform/DatabasePlatform.java b/src/main/java/io/ebean/config/dbplatform/DatabasePlatform.java
index c1a014095..b1ddadc2c 100644
--- a/src/main/java/io/ebean/config/dbplatform/DatabasePlatform.java
+++ b/src/main/java/io/ebean/config/dbplatform/DatabasePlatform.java
@@ -517,6 +517,13 @@ public class DatabasePlatform {
this.forwardOnlyHintOnFindIterate = forwardOnlyHintOnFindIterate;
}
+ /**
+ * Normally not needed - overridden in CockroachPlatform.
+ */
+ public boolean isDdlCommitOnCreateIndex() {
+ return false;
+ }
+
/**
* Return the DB identity/sequence features for this platform.
*
diff --git a/src/main/java/io/ebean/config/dbplatform/cockroach/CockroachPlatform.java b/src/main/java/io/ebean/config/dbplatform/cockroach/CockroachPlatform.java
new file mode 100644
index 000000000..502e33cf6
--- /dev/null
+++ b/src/main/java/io/ebean/config/dbplatform/cockroach/CockroachPlatform.java
@@ -0,0 +1,28 @@
+package io.ebean.config.dbplatform.cockroach;
+
+import io.ebean.annotation.Platform;
+import io.ebean.config.dbplatform.postgres.PostgresPlatform;
+
+/**
+ * CockroachDB based platform.
+ */
+public class CockroachPlatform extends PostgresPlatform {
+
+ public CockroachPlatform() {
+ super();
+ this.platform = Platform.COCKROACH;
+ // no like escape clause supported
+ this.likeSpecialCharacters = new char[]{'%', '_'};
+ this.likeClauseRaw = "like ?";
+ this.likeClauseEscaped = "like ?";
+ }
+
+ /**
+ * Needs a commit after create index such that alter table add foreign key ... succeeds.
+ */
+ @Override
+ public boolean isDdlCommitOnCreateIndex() {
+ return true;
+ }
+
+}
diff --git a/src/main/java/io/ebeaninternal/dbmigration/DdlGenerator.java b/src/main/java/io/ebeaninternal/dbmigration/DdlGenerator.java
index ae1ef4d08..68455dd89 100644
--- a/src/main/java/io/ebeaninternal/dbmigration/DdlGenerator.java
+++ b/src/main/java/io/ebeaninternal/dbmigration/DdlGenerator.java
@@ -37,6 +37,7 @@ public class DdlGenerator {
private final boolean runDdl;
private final boolean createOnly;
private final boolean jaxbPresent;
+ private final boolean ddlCommitOnCreateIndex;
private CurrentModel currentModel;
private String dropAllContent;
@@ -50,8 +51,10 @@ public class DdlGenerator {
if (!serverConfig.getTenantMode().isDdlEnabled() && serverConfig.isDdlRun()) {
log.warn("DDL can't be run on startup with TenantMode " + serverConfig.getTenantMode());
this.runDdl = false;
+ this.ddlCommitOnCreateIndex = false;
} else {
this.runDdl = serverConfig.isDdlRun();
+ this.ddlCommitOnCreateIndex = server.getDatabasePlatform().isDdlCommitOnCreateIndex();
}
}
@@ -109,6 +112,8 @@ public class DdlGenerator {
try {
if (expectErrors) {
connection.setAutoCommit(true);
+ } else if (ddlCommitOnCreateIndex) {
+ runner.setCommitOnCreateIndex();
}
int count = runner.runAll(content, connection);
if (expectErrors) {
diff --git a/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/CockroachDdl.java b/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/CockroachDdl.java
new file mode 100644
index 000000000..303c887bd
--- /dev/null
+++ b/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/CockroachDdl.java
@@ -0,0 +1,57 @@
+package io.ebeaninternal.dbmigration.ddlgeneration.platform;
+
+import io.ebean.config.dbplatform.DatabasePlatform;
+import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer;
+
+/**
+ * CockroachDB specific DDL handling.
+ */
+public class CockroachDdl extends PlatformDdl {
+
+ public CockroachDdl(DatabasePlatform platform) {
+ super(platform);
+ this.historyDdl = new PostgresHistoryDdl();
+ this.dropTableCascade = " cascade";
+ this.columnSetType = "type ";
+ this.alterTableIfExists = "if exists ";
+ this.columnSetNull = "drop not null";
+ }
+
+// @Override
+// protected String convertArrayType(String logicalArrayType) {
+// TODO: Need to look at DB Array support ...
+// }
+
+ /**
+ * Map bigint, integer and smallint all into serial.
+ */
+ @Override
+ public String asIdentityColumn(String columnDefn) {
+
+ if ("bigint".equalsIgnoreCase(columnDefn)) {
+ return "serial";
+ }
+ if ("integer".equalsIgnoreCase(columnDefn)) {
+ return "serial";
+ }
+ if ("smallint".equalsIgnoreCase(columnDefn)) {
+ return "serial";
+ }
+ return columnDefn;
+ }
+
+ @Override
+ public void addTableComment(DdlBuffer apply, String tableName, String tableComment) {
+ // do nothing
+ }
+
+ @Override
+ public void addColumnComment(DdlBuffer apply, String table, String column, String comment) {
+ // do nothing
+ }
+
+ @Override
+ public boolean isInlineComments() {
+ return false;
+ }
+}
diff --git a/src/main/java/io/ebeaninternal/server/core/DatabasePlatformFactory.java b/src/main/java/io/ebeaninternal/server/core/DatabasePlatformFactory.java
index 3f98a1c6d..75db54f34 100644
--- a/src/main/java/io/ebeaninternal/server/core/DatabasePlatformFactory.java
+++ b/src/main/java/io/ebeaninternal/server/core/DatabasePlatformFactory.java
@@ -2,6 +2,7 @@ package io.ebeaninternal.server.core;
import io.ebean.config.ServerConfig;
import io.ebean.config.dbplatform.DatabasePlatform;
+import io.ebean.config.dbplatform.cockroach.CockroachPlatform;
import io.ebean.config.dbplatform.db2.DB2Platform;
import io.ebean.config.dbplatform.h2.H2Platform;
import io.ebean.config.dbplatform.hsqldb.HsqldbPlatform;
@@ -13,6 +14,7 @@ import io.ebean.config.dbplatform.sqlanywhere.SqlAnywherePlatform;
import io.ebean.config.dbplatform.sqlite.SQLitePlatform;
import io.ebean.config.dbplatform.sqlserver.SqlServer16Platform;
import io.ebean.config.dbplatform.sqlserver.SqlServer17Platform;
+import io.ebean.migration.util.JdbcClose;
import io.ebeaninternal.dbmigration.DbOffline;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -21,6 +23,8 @@ import javax.persistence.PersistenceException;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
import java.sql.SQLException;
/**
@@ -111,20 +115,20 @@ public class DatabasePlatformFactory {
*/
private DatabasePlatform byDataSource(DataSource dataSource) {
- Connection conn = null;
+ Connection connection = null;
try {
- conn = dataSource.getConnection();
- DatabaseMetaData metaData = conn.getMetaData();
+ connection = dataSource.getConnection();
+ DatabaseMetaData metaData = connection.getMetaData();
- return byDatabaseMeta(metaData);
+ return byDatabaseMeta(metaData, connection);
} catch (SQLException ex) {
throw new PersistenceException(ex);
} finally {
try {
- if (conn != null) {
- conn.close();
+ if (connection != null) {
+ connection.close();
}
} catch (SQLException ex) {
logger.error(null, ex);
@@ -135,7 +139,7 @@ public class DatabasePlatformFactory {
/**
* Find the platform by the metaData.getDatabaseProductName().
*/
- private DatabasePlatform byDatabaseMeta(DatabaseMetaData metaData) throws SQLException {
+ private DatabasePlatform byDatabaseMeta(DatabaseMetaData metaData, Connection connection) throws SQLException {
String dbProductName = metaData.getDatabaseProductName();
dbProductName = dbProductName.toLowerCase();
@@ -151,7 +155,7 @@ public class DatabasePlatformFactory {
} else if (dbProductName.contains("hsql database engine")) {
return new HsqldbPlatform();
} else if (dbProductName.contains("postgres")) {
- return new PostgresPlatform();
+ return readPostgres(connection);
} else if (dbProductName.contains("sqlite")) {
return new SQLitePlatform();
} else if (dbProductName.contains("db2")) {
@@ -163,4 +167,32 @@ public class DatabasePlatformFactory {
// use the standard one
return new DatabasePlatform();
}
+
+ /**
+ * Use a select version() query as it could be Postgres or CockroachDB.
+ */
+ private static PostgresPlatform readPostgres(Connection connection) {
+ // Postgres driver uses a hardcoded product name so use version() query
+ PreparedStatement statement = null;
+ ResultSet resultSet = null;
+ try {
+ statement = connection.prepareStatement("SELECT version() AS \"version\"");
+ resultSet = statement.executeQuery();
+ if (resultSet.next()) {
+ String productVersion = resultSet.getString("version").toLowerCase();
+ if (productVersion.contains("cockroach")) {
+ return new CockroachPlatform();
+ }
+ }
+ } catch (SQLException e) {
+ logger.warn("Error running detection query on Postgres", e);
+
+ } finally {
+ JdbcClose.close(resultSet);
+ JdbcClose.close(statement);
+ }
+
+ // Real Postgres
+ return new PostgresPlatform();
+ }
}
diff --git a/src/main/java/io/ebeaninternal/server/core/PlatformDdlBuilder.java b/src/main/java/io/ebeaninternal/server/core/PlatformDdlBuilder.java
index efd69538e..bf901ed50 100644
--- a/src/main/java/io/ebeaninternal/server/core/PlatformDdlBuilder.java
+++ b/src/main/java/io/ebeaninternal/server/core/PlatformDdlBuilder.java
@@ -1,6 +1,7 @@
package io.ebeaninternal.server.core;
import io.ebean.config.dbplatform.DatabasePlatform;
+import io.ebeaninternal.dbmigration.ddlgeneration.platform.CockroachDdl;
import io.ebeaninternal.dbmigration.ddlgeneration.platform.DB2Ddl;
import io.ebeaninternal.dbmigration.ddlgeneration.platform.H2Ddl;
import io.ebeaninternal.dbmigration.ddlgeneration.platform.HsqldbDdl;
@@ -38,6 +39,8 @@ public class PlatformDdlBuilder {
return new PlatformDdl(platform);
case POSTGRES:
return new PostgresDdl(platform);
+ case COCKROACH:
+ return new CockroachDdl(platform);
case SQLANYWHERE:
return new PlatformDdl(platform);
case SQLSERVER16: