From 64605b5090945ab9e7cdc13342e05428f8192587 Mon Sep 17 00:00:00 2001 From: Rob Bygrave Date: Fri, 28 Jan 2022 17:04:19 +1300 Subject: [PATCH] Yugabyte platform support - Automatic detection and DDL support - Automatic detection similar to Cockroach - DDL very similar to Postgres but no CREATE TABLE LIKE support (for history DDL) --- ebean-api/pom.xml | 2 +- .../dbplatform/yugabyte/YugabytePlaform.java | 12 ++++++++ .../server/core/DatabasePlatformFactory.java | 12 ++++++-- .../platform/DbExpressionHandlerFactory.java | 2 ++ .../ddlgeneration/PlatformDdlBuilder.java | 18 ++---------- .../platform/DbTriggerBasedHistoryDdl.java | 11 ++++++-- .../platform/PostgresHistoryDdl.java | 12 ++------ .../ddlgeneration/platform/YugabyteDdl.java | 11 ++++++++ .../platform/YugabyteHistoryDdl.java | 16 +++++++++++ ebean-test/pom.xml | 2 +- .../src/test/java/main/StartYugabyte.java | 28 +++++++++++++++++++ .../src/test/resources/ebean.properties | 5 ++++ 12 files changed, 98 insertions(+), 33 deletions(-) create mode 100644 ebean-api/src/main/java/io/ebean/config/dbplatform/yugabyte/YugabytePlaform.java create mode 100644 ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/YugabyteDdl.java create mode 100644 ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/YugabyteHistoryDdl.java create mode 100644 ebean-test/src/test/java/main/StartYugabyte.java diff --git a/ebean-api/pom.xml b/ebean-api/pom.xml index 15021541b..b914e2c2b 100644 --- a/ebean-api/pom.xml +++ b/ebean-api/pom.xml @@ -49,7 +49,7 @@ io.ebean ebean-annotation - 7.5 + 7.6 diff --git a/ebean-api/src/main/java/io/ebean/config/dbplatform/yugabyte/YugabytePlaform.java b/ebean-api/src/main/java/io/ebean/config/dbplatform/yugabyte/YugabytePlaform.java new file mode 100644 index 000000000..622f8e5f8 --- /dev/null +++ b/ebean-api/src/main/java/io/ebean/config/dbplatform/yugabyte/YugabytePlaform.java @@ -0,0 +1,12 @@ +package io.ebean.config.dbplatform.yugabyte; + +import io.ebean.annotation.Platform; +import io.ebean.config.dbplatform.postgres.PostgresPlatform; + +public class YugabytePlaform extends PostgresPlatform { + + public YugabytePlaform() { + super(); + this.platform = Platform.YUGABYTE; + } +} diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/core/DatabasePlatformFactory.java b/ebean-core/src/main/java/io/ebeaninternal/server/core/DatabasePlatformFactory.java index ad03fd45f..5b49d5292 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/core/DatabasePlatformFactory.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/core/DatabasePlatformFactory.java @@ -24,12 +24,14 @@ 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.config.dbplatform.yugabyte.YugabytePlaform; import io.ebeaninternal.api.CoreLog; import io.ebeaninternal.api.DbOffline; import javax.persistence.PersistenceException; import javax.sql.DataSource; import java.sql.*; +import java.util.Locale; /** * Create a DatabasePlatform from the configuration. @@ -164,7 +166,8 @@ public class DatabasePlatformFactory { } else if (dbProductName.contains("hsql database engine")) { return new HsqldbPlatform(); } else if (dbProductName.contains("postgres")) { - return readPostgres(connection, majorVersion); + String productVersion = metaData.getDatabaseProductVersion().toLowerCase(Locale.ENGLISH); + return readPostgres(connection, majorVersion, productVersion); } else if (dbProductName.contains("mariadb")) { return new MariaDbPlatform(); } else if (dbProductName.contains("mysql")) { @@ -207,11 +210,14 @@ public class DatabasePlatformFactory { /** * Use a select version() query as it could be Postgres or CockroachDB. */ - private static DatabasePlatform readPostgres(Connection connection, int majorVersion) { + private static DatabasePlatform readPostgres(Connection connection, int majorVersion, String productVersion) { + if (productVersion.contains("-yb-")) { + return new YugabytePlaform(); + } try (PreparedStatement statement = connection.prepareStatement("select version() as \"version\"")) { try (ResultSet resultSet = statement.executeQuery()) { if (resultSet.next()) { - String productVersion = resultSet.getString("version").toLowerCase(); + productVersion = resultSet.getString("version").toLowerCase(); if (productVersion.contains("cockroach")) { return new CockroachPlatform(); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/platform/DbExpressionHandlerFactory.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/platform/DbExpressionHandlerFactory.java index c555e33c5..1af660cae 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/platform/DbExpressionHandlerFactory.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/platform/DbExpressionHandlerFactory.java @@ -15,6 +15,8 @@ public final class DbExpressionHandlerFactory { case H2: return new H2DbExpression(); case POSTGRES: + case YUGABYTE: + case COCKROACH: return new PostgresDbExpression(); case MARIADB: return new MariaDbExpression(); diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/PlatformDdlBuilder.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/PlatformDdlBuilder.java index 0133413e0..07e0be2bf 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/PlatformDdlBuilder.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/PlatformDdlBuilder.java @@ -1,21 +1,7 @@ package io.ebeaninternal.dbmigration.ddlgeneration; import io.ebean.config.dbplatform.DatabasePlatform; -import io.ebeaninternal.dbmigration.ddlgeneration.platform.ClickHouseDdl; -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.HanaColumnStoreDdl; -import io.ebeaninternal.dbmigration.ddlgeneration.platform.HsqldbDdl; -import io.ebeaninternal.dbmigration.ddlgeneration.platform.MariaDbDdl; -import io.ebeaninternal.dbmigration.ddlgeneration.platform.MySqlDdl; -import io.ebeaninternal.dbmigration.ddlgeneration.platform.NuoDbDdl; -import io.ebeaninternal.dbmigration.ddlgeneration.platform.OracleDdl; -import io.ebeaninternal.dbmigration.ddlgeneration.platform.PlatformDdl; -import io.ebeaninternal.dbmigration.ddlgeneration.platform.Postgres9Ddl; -import io.ebeaninternal.dbmigration.ddlgeneration.platform.PostgresDdl; -import io.ebeaninternal.dbmigration.ddlgeneration.platform.SQLiteDdl; -import io.ebeaninternal.dbmigration.ddlgeneration.platform.SqlServerDdl; +import io.ebeaninternal.dbmigration.ddlgeneration.platform.*; /** * Builds platform specific DDL handler. @@ -53,6 +39,8 @@ public class PlatformDdlBuilder { return new Postgres9Ddl(platform); case POSTGRES: return new PostgresDdl(platform); + case YUGABYTE: + return new YugabyteDdl(platform); case COCKROACH: return new CockroachDdl(platform); case SQLSERVER16: diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/DbTriggerBasedHistoryDdl.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/DbTriggerBasedHistoryDdl.java index 575deb5a1..58bed4505 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/DbTriggerBasedHistoryDdl.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/DbTriggerBasedHistoryDdl.java @@ -174,16 +174,21 @@ public abstract class DbTriggerBasedHistoryDdl implements PlatformHistoryDdl { } protected void createHistoryTable(DdlBuffer apply, MTable table) throws IOException { + createHistoryTableAs(apply, table); + createHistoryTableWithPeriod(apply); + } + protected void createHistoryTableAs(DdlBuffer apply, MTable table) throws IOException { apply.append(platformDdl.getCreateTableCommandPrefix()).append(" ").append(table.getName()).append(historySuffix).append("(").newLine(); - - Collection cols = table.allColumns(); - for (MColumn column : cols) { + for (MColumn column : table.allColumns()) { if (!column.isDraftOnly()) { writeColumnDefinition(apply, column.getName(), column.getType()); apply.append(",").newLine(); } } + } + + protected void createHistoryTableWithPeriod(DdlBuffer apply) throws IOException { writeColumnDefinition(apply, sysPeriodStart, sysPeriodType); apply.append(",").newLine(); writeColumnDefinition(apply, sysPeriodEnd, sysPeriodType); diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/PostgresHistoryDdl.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/PostgresHistoryDdl.java index fe9b17f1b..b0257f96a 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/PostgresHistoryDdl.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/PostgresHistoryDdl.java @@ -22,11 +22,8 @@ public class PostgresHistoryDdl extends DbTriggerBasedHistoryDdl { */ @Override protected void createHistoryTable(DdlBuffer apply, MTable table) throws IOException { - - String baseTable = table.getName(); - apply - .append("create table ").append(baseTable).append(historySuffix) - .append("(like ").append(baseTable).append(")").endOfStatement(); + apply.append("create table ").append(table.getName()).append(historySuffix) + .append("(like ").append(table.getName()).append(")").endOfStatement(); } /** @@ -57,7 +54,6 @@ public class PostgresHistoryDdl extends DbTriggerBasedHistoryDdl { @Override protected void createTriggers(DdlWrite writer, MTable table) throws IOException { - String baseTableName = table.getName(); String procedureName = procedureName(baseTableName); String triggerName = triggerName(baseTableName); @@ -110,7 +106,6 @@ public class PostgresHistoryDdl extends DbTriggerBasedHistoryDdl { @Override protected void createStoredFunction(DdlWrite writer, MTable table) throws IOException { - String procedureName = procedureName(table.getName()); String historyTable = historyTableName(table.getName()); @@ -120,16 +115,13 @@ public class PostgresHistoryDdl extends DbTriggerBasedHistoryDdl { @Override protected void updateHistoryTriggers(DbTriggerUpdate update) throws IOException { - String procedureName = procedureName(update.getBaseTable()); - recreateHistoryView(update); createOrReplaceFunction(update.historyTriggerBuffer(), procedureName, update.getHistoryTable(), update.getColumns()); } @Override protected void appendInsertIntoHistory(DdlBuffer buffer, String historyTable, List columns) throws IOException { - buffer.append(" insert into ").append(historyTable).append(" (").append(sysPeriod).append(","); appendColumnNames(buffer, columns, ""); buffer.append(") values (tstzrange(lowerTs,upperTs), "); diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/YugabyteDdl.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/YugabyteDdl.java new file mode 100644 index 000000000..a78e5b339 --- /dev/null +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/YugabyteDdl.java @@ -0,0 +1,11 @@ +package io.ebeaninternal.dbmigration.ddlgeneration.platform; + +import io.ebean.config.dbplatform.DatabasePlatform; + +public class YugabyteDdl extends PostgresDdl { + + public YugabyteDdl(DatabasePlatform platform) { + super(platform); + this.historyDdl = new YugabyteHistoryDdl(); + } +} diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/YugabyteHistoryDdl.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/YugabyteHistoryDdl.java new file mode 100644 index 000000000..3ba023881 --- /dev/null +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/YugabyteHistoryDdl.java @@ -0,0 +1,16 @@ +package io.ebeaninternal.dbmigration.ddlgeneration.platform; + +import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer; +import io.ebeaninternal.dbmigration.model.MTable; + +import java.io.IOException; + +public class YugabyteHistoryDdl extends PostgresHistoryDdl { + + @Override + protected void createHistoryTable(DdlBuffer apply, MTable table) throws IOException { + createHistoryTableAs(apply, table); + writeColumnDefinition(apply, sysPeriod, "tstzrange"); + apply.newLine().append(")").endOfStatement(); + } +} diff --git a/ebean-test/pom.xml b/ebean-test/pom.xml index 2fcf19018..caeaaa5f7 100644 --- a/ebean-test/pom.xml +++ b/ebean-test/pom.xml @@ -56,7 +56,7 @@ io.ebean ebean-test-docker - 4.4 + 4.5 diff --git a/ebean-test/src/test/java/main/StartYugabyte.java b/ebean-test/src/test/java/main/StartYugabyte.java new file mode 100644 index 000000000..ed3d4ec46 --- /dev/null +++ b/ebean-test/src/test/java/main/StartYugabyte.java @@ -0,0 +1,28 @@ +package main; + +import io.ebean.docker.commands.YugabyteConfig; +import io.ebean.docker.commands.YugabyteContainer; + +public class StartYugabyte { + + public static void main(String[] args) { + + YugabyteConfig config = new YugabyteConfig("2.11.2.0-b89"); + config.setDbName("unit"); + config.setUser("unit"); + + YugabyteContainer container = new YugabyteContainer(config); + container.start(); + +// Run container ut_yugabyte with host:localhost port:6433 db:unit user:unit/test shutdown:None +// docker run -d --name ut_yugabyte -p 6433:5433 -p 7000:7000 -p 9000:9000 -p 9042:9042 yugabytedb/yugabyte:2.11.2.0-b89 bin/yugabyted start --daemon=false +// ... +// Commands - sqlRun: drop database if exists unit +// Commands - sqlRun: drop role if exists unit +// Commands - sqlRun: select 1 from pg_database where datname = 'unit' +// Commands - sqlRun: select rolname from pg_roles where rolname = 'unit' +// Commands - sqlRun: create role unit password 'test' login createrole +// Commands - sqlRun: create database unit with owner unit + + } +} diff --git a/ebean-test/src/test/resources/ebean.properties b/ebean-test/src/test/resources/ebean.properties index d7e1debae..2b8bdfa45 100644 --- a/ebean-test/src/test/resources/ebean.properties +++ b/ebean-test/src/test/resources/ebean.properties @@ -23,6 +23,7 @@ ebean.ddl.run=true ebean.ddl.header=-- Generated by ebean ${version} at ${timestamp} ebean.packages=org.tests,org.etest datasource.default=h2 +#datasource.default=yugabyte #datasource.h2.capturestacktrace=true #ebean.dumpMetricsOnShutdown=true @@ -144,6 +145,10 @@ datasource.cockroach.password= datasource.cockroach.url=jdbc:postgresql://localhost:26257/unit?sslmode=disable datasource.cockroach.driver=org.postgresql.Driver +datasource.yugabyte.username=unit +datasource.yugabyte.password=test +datasource.yugabyte.url=jdbc:postgresql://localhost:6433/unit?sslmode=disable + datasource.oracle.username=test_ebean datasource.oracle.password=test datasource.oracle.url=jdbc:oracle:thin:@localhost:1521:XE