diff --git a/pom.xml b/pom.xml index 769a9e4fb..e4b3bd468 100644 --- a/pom.xml +++ b/pom.xml @@ -248,6 +248,13 @@ test + + org.mariadb.jdbc + mariadb-java-client + 2.6.0 + test + + com.sap.cloud.db.jdbc ngdbc diff --git a/src/main/java/io/ebean/config/dbplatform/mariadb/MariaDbHistorySupport.java b/src/main/java/io/ebean/config/dbplatform/mariadb/MariaDbHistorySupport.java new file mode 100644 index 000000000..4a8210863 --- /dev/null +++ b/src/main/java/io/ebean/config/dbplatform/mariadb/MariaDbHistorySupport.java @@ -0,0 +1,38 @@ +package io.ebean.config.dbplatform.mariadb; + +import io.ebean.config.dbplatform.DbStandardHistorySupport; + +/** + * History support for MariaDB. + */ +public class MariaDbHistorySupport extends DbStandardHistorySupport { + + /** + * Return the ' as of timestamp ?' clause appended after the table name. + */ + @Override + public String getAsOfViewSuffix(String asOfViewSuffix) { + return " for system_time as of ?"; + } + + @Override + public String getVersionsBetweenSuffix(String asOfViewSuffix) { + return " for system_time between ? and ?"; + } + + /** + * Returns the SQL Server specific effective start column. + */ + @Override + public String getSysPeriodLower(String tableAlias, String sysPeriod) { + return tableAlias + "." + sysPeriod + "From"; + } + + /** + * Returns the SQL Server specific effective end column. + */ + @Override + public String getSysPeriodUpper(String tableAlias, String sysPeriod) { + return tableAlias + "." + sysPeriod + "To"; + } +} diff --git a/src/main/java/io/ebean/config/dbplatform/mariadb/MariaDbPlatform.java b/src/main/java/io/ebean/config/dbplatform/mariadb/MariaDbPlatform.java new file mode 100644 index 000000000..082f31c11 --- /dev/null +++ b/src/main/java/io/ebean/config/dbplatform/mariadb/MariaDbPlatform.java @@ -0,0 +1,16 @@ +package io.ebean.config.dbplatform.mariadb; + +import io.ebean.annotation.Platform; +import io.ebean.config.dbplatform.mysql.BaseMySqlPlatform; + +/** + * MariaDB platform. + */ +public class MariaDbPlatform extends BaseMySqlPlatform { + + public MariaDbPlatform() { + super(); + this.platform = Platform.MARIADB; + this.historySupport = new MariaDbHistorySupport(); + } +} diff --git a/src/main/java/io/ebean/config/dbplatform/mysql/BaseMySqlPlatform.java b/src/main/java/io/ebean/config/dbplatform/mysql/BaseMySqlPlatform.java new file mode 100644 index 000000000..7cd33008c --- /dev/null +++ b/src/main/java/io/ebean/config/dbplatform/mysql/BaseMySqlPlatform.java @@ -0,0 +1,68 @@ +package io.ebean.config.dbplatform.mysql; + +import io.ebean.Query; +import io.ebean.config.dbplatform.DatabasePlatform; +import io.ebean.config.dbplatform.DbPlatformType; +import io.ebean.config.dbplatform.DbType; +import io.ebean.config.dbplatform.IdType; +import io.ebean.config.dbplatform.SqlErrorCodes; + +import java.sql.Types; + +/** + * Base platform for both MySql and MariaDB. + */ +public abstract class BaseMySqlPlatform extends DatabasePlatform { + + public BaseMySqlPlatform() { + super(); + this.useExtraTransactionOnIterateSecondaryQueries = true; + this.selectCountWithAlias = true; + this.supportsSavepointId = false; + this.inlineSqlUpdateLimit = true; + this.dbEncrypt = new MySqlDbEncrypt(); + this.historySupport = new MySqlHistorySupport(); + this.dbIdentity.setIdType(IdType.IDENTITY); + this.dbIdentity.setSupportsGetGeneratedKeys(true); + this.dbIdentity.setSupportsIdentity(true); + this.dbIdentity.setSupportsSequence(false); + + this.dbDefaultValue.setNow("now(6)"); // must have same precision as TIMESTAMP + this.dbDefaultValue.setFalse("0"); + this.dbDefaultValue.setTrue("1"); + + + this.exceptionTranslator = + new SqlErrorCodes() + .addAcquireLock("1205") + .addDuplicateKey("1062", "1169") + .addDataIntegrity("630", "839", "840", "893", "1215", "1216", "1217", "1364", "1451", "1452", "1557") + .build(); + + this.openQuote = "`"; + this.closeQuote = "`"; + // use pipe for escaping as it depends if mysql runs in no_backslash_escapes or not. + this.likeClauseRaw = "like ? escape''"; + this.likeClauseEscaped = "like ? escape'|'"; + + this.forwardOnlyHintOnFindIterate = true; + this.booleanDbType = Types.BIT; + + dbTypeMap.put(DbType.BIT, new DbPlatformType("tinyint(1)")); + dbTypeMap.put(DbType.BOOLEAN, new DbPlatformType("tinyint(1)")); + dbTypeMap.put(DbType.TIMESTAMP, new DbPlatformType("datetime(6)")); + dbTypeMap.put(DbType.CLOB, new MySqlClob()); + dbTypeMap.put(DbType.BLOB, new MySqlBlob()); + dbTypeMap.put(DbType.BINARY, new DbPlatformType("binary", 255)); + dbTypeMap.put(DbType.VARBINARY, new DbPlatformType("varbinary", 255)); + dbTypeMap.put(DbType.JSON, new DbPlatformType("json", false)); + dbTypeMap.put(DbType.JSONB, new DbPlatformType("json", false)); + } + + @Override + protected String withForUpdate(String sql, Query.ForUpdate forUpdateMode) { + // NOWAIT and SKIP LOCKED currently not supported with MySQL + return sql + " for update"; + } + +} diff --git a/src/main/java/io/ebean/config/dbplatform/mysql/MySql55Platform.java b/src/main/java/io/ebean/config/dbplatform/mysql/MySql55Platform.java index f1d24b768..780668f02 100644 --- a/src/main/java/io/ebean/config/dbplatform/mysql/MySql55Platform.java +++ b/src/main/java/io/ebean/config/dbplatform/mysql/MySql55Platform.java @@ -4,7 +4,7 @@ import io.ebean.annotation.Platform; import io.ebean.config.dbplatform.DbPlatformType; import io.ebean.config.dbplatform.DbType; -public class MySql55Platform extends MySqlPlatform { +public class MySql55Platform extends BaseMySqlPlatform { public MySql55Platform() { super(); diff --git a/src/main/java/io/ebean/config/dbplatform/mysql/MySqlPlatform.java b/src/main/java/io/ebean/config/dbplatform/mysql/MySqlPlatform.java index 944da83ab..be4aaa166 100644 --- a/src/main/java/io/ebean/config/dbplatform/mysql/MySqlPlatform.java +++ b/src/main/java/io/ebean/config/dbplatform/mysql/MySqlPlatform.java @@ -1,77 +1,15 @@ package io.ebean.config.dbplatform.mysql; -import io.ebean.Query; import io.ebean.annotation.Platform; -import io.ebean.config.dbplatform.DatabasePlatform; -import io.ebean.config.dbplatform.DbPlatformType; -import io.ebean.config.dbplatform.DbType; -import io.ebean.config.dbplatform.IdType; -import io.ebean.config.dbplatform.SqlErrorCodes; - -import java.sql.Types; /** * MySQL specific platform. - *

- *

- *

*/ -public class MySqlPlatform extends DatabasePlatform { +public class MySqlPlatform extends BaseMySqlPlatform { public MySqlPlatform() { super(); this.platform = Platform.MYSQL; - this.useExtraTransactionOnIterateSecondaryQueries = true; - this.selectCountWithAlias = true; - this.supportsSavepointId = false; - this.inlineSqlUpdateLimit = true; - this.dbEncrypt = new MySqlDbEncrypt(); - this.historySupport = new MySqlHistorySupport(); - this.dbIdentity.setIdType(IdType.IDENTITY); - this.dbIdentity.setSupportsGetGeneratedKeys(true); - this.dbIdentity.setSupportsIdentity(true); - this.dbIdentity.setSupportsSequence(false); - - this.dbDefaultValue.setNow("now(6)"); // must have same precision as TIMESTAMP - this.dbDefaultValue.setFalse("0"); - this.dbDefaultValue.setTrue("1"); - - - this.exceptionTranslator = - new SqlErrorCodes() - .addAcquireLock("1205") - .addDuplicateKey("1062", "1169") - .addDataIntegrity("630", "839", "840", "893", "1215", "1216", "1217", "1364", "1451", "1452", "1557") - .build(); - - this.openQuote = "`"; - this.closeQuote = "`"; - // use pipe for escaping as it depends if mysql runs in no_backslash_escapes or not. - this.likeClauseRaw = "like ? escape''"; - this.likeClauseEscaped = "like ? escape'|'"; - - this.forwardOnlyHintOnFindIterate = true; - this.booleanDbType = Types.BIT; - - dbTypeMap.put(DbType.BIT, new DbPlatformType("tinyint(1)")); - dbTypeMap.put(DbType.BOOLEAN, new DbPlatformType("tinyint(1)")); - dbTypeMap.put(DbType.TIMESTAMP, new DbPlatformType("datetime(6)")); - dbTypeMap.put(DbType.CLOB, new MySqlClob()); - dbTypeMap.put(DbType.BLOB, new MySqlBlob()); - dbTypeMap.put(DbType.BINARY, new DbPlatformType("binary", 255)); - dbTypeMap.put(DbType.VARBINARY, new DbPlatformType("varbinary", 255)); - dbTypeMap.put(DbType.JSON, new DbPlatformType("json", false)); - dbTypeMap.put(DbType.JSONB, new DbPlatformType("json", false)); - } - - @Override - protected String withForUpdate(String sql, Query.ForUpdate forUpdateMode) { - // NOWAIT and SKIP LOCKED currently not supported with MySQL - return sql + " for update"; } } diff --git a/src/main/java/io/ebeaninternal/dbmigration/DefaultDbMigration.java b/src/main/java/io/ebeaninternal/dbmigration/DefaultDbMigration.java index 7b36be18b..8130293ef 100644 --- a/src/main/java/io/ebeaninternal/dbmigration/DefaultDbMigration.java +++ b/src/main/java/io/ebeaninternal/dbmigration/DefaultDbMigration.java @@ -15,6 +15,7 @@ import io.ebean.config.dbplatform.db2.DB2Platform; import io.ebean.config.dbplatform.h2.H2Platform; import io.ebean.config.dbplatform.hana.HanaPlatform; import io.ebean.config.dbplatform.hsqldb.HsqldbPlatform; +import io.ebean.config.dbplatform.mariadb.MariaDbPlatform; import io.ebean.config.dbplatform.mysql.MySql55Platform; import io.ebean.config.dbplatform.mysql.MySqlPlatform; import io.ebean.config.dbplatform.nuodb.NuoDbPlatform; @@ -820,6 +821,8 @@ public class DefaultDbMigration implements DbMigration { return new Postgres9Platform(); case POSTGRES: return new PostgresPlatform(); + case MARIADB: + return new MariaDbPlatform(); case MYSQL55: return new MySql55Platform(); case MYSQL: diff --git a/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/MariaDbDdl.java b/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/MariaDbDdl.java new file mode 100644 index 000000000..a4f5775b5 --- /dev/null +++ b/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/MariaDbDdl.java @@ -0,0 +1,14 @@ +package io.ebeaninternal.dbmigration.ddlgeneration.platform; + +import io.ebean.config.dbplatform.DatabasePlatform; + +/** + * MariaDB platform DDL. + */ +public class MariaDbDdl extends MySqlDdl { + + public MariaDbDdl(DatabasePlatform platform) { + super(platform); + this.historyDdl = new MariaDbHistoryDdl(); + } +} diff --git a/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/MariaDbHistoryDdl.java b/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/MariaDbHistoryDdl.java new file mode 100644 index 000000000..e33e53686 --- /dev/null +++ b/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/MariaDbHistoryDdl.java @@ -0,0 +1,98 @@ +package io.ebeaninternal.dbmigration.ddlgeneration.platform; + +import io.ebean.config.ServerConfig; +import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer; +import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite; +import io.ebeaninternal.dbmigration.migration.AddHistoryTable; +import io.ebeaninternal.dbmigration.migration.DropHistoryTable; +import io.ebeaninternal.dbmigration.model.MTable; + +import java.io.IOException; + +/** + * History DDL for MariaDB. + */ +public class MariaDbHistoryDdl implements PlatformHistoryDdl { + + private String systemPeriodStart; + private String systemPeriodEnd; + private PlatformDdl platformDdl; + + @Override + public void configure(ServerConfig serverConfig, PlatformDdl platformDdl) { + this.systemPeriodStart = serverConfig.getAsOfSysPeriod() + "From"; + this.systemPeriodEnd = serverConfig.getAsOfSysPeriod() + "To"; + this.platformDdl = platformDdl; + } + + @Override + public void createWithHistory(DdlWrite writer, MTable table) throws IOException { + String baseTable = table.getName(); + enableSystemVersioning(writer, baseTable); + } + + String getHistoryTable(String baseTable) { + String historyTable = baseTable + "_history"; + if (baseTable.startsWith("[")) { + historyTable = historyTable.replace("]", "") + "]"; + } + if (historyTable.indexOf('.') == -1) { + // history must contain schema, add the default schema if none was specified + historyTable = "dbo." + historyTable; + } + return historyTable; + } + + private void enableSystemVersioning(DdlWrite writer, String baseTable) throws IOException { + DdlBuffer apply = writer.applyHistoryView(); + + final String systemTime = String.format("system_time(%s, %s),", systemPeriodStart, systemPeriodEnd); + apply.append("alter table ").append(baseTable).newLine() + .append(" add column ").append(systemPeriodStart).append(" timestamp(6) generated always as row start,").newLine() + .append(" add column ").append(systemPeriodEnd).append(" timestamp(6) generated always as row end,").newLine() + .append(" add period for ").append(systemTime).newLine() + .append(" add system versioning;").newLine(); + + DdlBuffer drop = writer.dropAll(); + //drop.append("IF OBJECT_ID('").append(baseTable).append("', 'U') IS NOT NULL alter table ").append(baseTable).append(" set (system_versioning = off)").endOfStatement(); + //drop.append("IF OBJECT_ID('").append(baseTable).append("_history', 'U') IS NOT NULL drop table ").append(baseTable).append("_history").endOfStatement(); + } + + @Override + public void dropHistoryTable(DdlWrite writer, DropHistoryTable dropHistoryTable) throws IOException { + String baseTable = dropHistoryTable.getBaseTable(); + DdlBuffer apply = writer.applyHistoryView(); + apply.append("-- dropping history support for ").append(baseTable).endOfStatement(); + // drop default constraints + + apply.append(platformDdl.alterColumnDefaultValue(baseTable, systemPeriodStart, DdlHelp.DROP_DEFAULT)).endOfStatement(); + apply.append(platformDdl.alterColumnDefaultValue(baseTable, systemPeriodEnd, DdlHelp.DROP_DEFAULT)).endOfStatement(); + // switch of versioning & period + apply.append("alter table ").append(baseTable).append(" set (system_versioning = off)").endOfStatement(); + apply.append("alter table ").append(baseTable).append(" drop period for system_time").endOfStatement(); + // now drop tables & columns + apply.append("alter table ").append(baseTable).append(" drop column ").append(systemPeriodStart).endOfStatement(); + apply.append("alter table ").append(baseTable).append(" drop column ").append(systemPeriodEnd).endOfStatement(); + //apply.append("IF OBJECT_ID('").append(baseTable).append("_history', 'U') IS NOT NULL drop table ").append(baseTable).append("_history").endOfStatement(); + apply.end(); + } + + @Override + public void addHistoryTable(DdlWrite writer, AddHistoryTable addHistoryTable) throws IOException { + String baseTable = addHistoryTable.getBaseTable(); + enableSystemVersioning(writer, baseTable); + } + + @Override + public void updateTriggers(DdlWrite writer, HistoryTableUpdate baseTable) throws IOException { + // SQL Server 2016 does not need triggers + DdlBuffer apply = writer.applyHistoryView(); + String baseTableName = baseTable.getBaseTable(); + apply.append("-- alter table ").append(baseTableName).append(" set (system_versioning = off (history_table=") + .append(getHistoryTable(baseTableName)).append("))").endOfStatement(); + apply.append("-- history migration goes here").newLine(); + apply.append("-- alter table ").append(baseTableName).append(" set (system_versioning = on (history_table=") + .append(getHistoryTable(baseTableName)).append("))").endOfStatement(); + + } +} diff --git a/src/main/java/io/ebeaninternal/server/core/DatabasePlatformFactory.java b/src/main/java/io/ebeaninternal/server/core/DatabasePlatformFactory.java index 15858e8d9..7c65d7f0e 100644 --- a/src/main/java/io/ebeaninternal/server/core/DatabasePlatformFactory.java +++ b/src/main/java/io/ebeaninternal/server/core/DatabasePlatformFactory.java @@ -8,6 +8,7 @@ import io.ebean.config.dbplatform.db2.DB2Platform; import io.ebean.config.dbplatform.h2.H2Platform; import io.ebean.config.dbplatform.hana.HanaPlatform; import io.ebean.config.dbplatform.hsqldb.HsqldbPlatform; +import io.ebean.config.dbplatform.mariadb.MariaDbPlatform; import io.ebean.config.dbplatform.mysql.MySql55Platform; import io.ebean.config.dbplatform.mysql.MySqlPlatform; import io.ebean.config.dbplatform.nuodb.NuoDbPlatform; @@ -76,11 +77,13 @@ public class DatabasePlatformFactory { * Lookup the platform by name. */ private DatabasePlatform byDatabaseName(String dbName) { - dbName = dbName.toLowerCase(); if (dbName.equals("h2")) { return new H2Platform(); } + if (dbName.equals("mariadb")) { + return new MariaDbPlatform(); + } if (dbName.equals("mysql")) { return new MySqlPlatform(); } @@ -123,7 +126,6 @@ public class DatabasePlatformFactory { if (dbName.equals("hana")) { return new HanaPlatform(); } - throw new RuntimeException("database platform " + dbName + " is not known?"); } @@ -160,14 +162,16 @@ public class DatabasePlatformFactory { return new OraclePlatform(); } else if (dbProductName.contains("microsoft")) { throw new IllegalArgumentException("For SqlServer please explicitly choose either sqlserver16 or sqlserver17 as the platform via ServerConfig.setDatabasePlatformName. Refer to issue #1340 for more details"); - } else if (dbProductName.contains("mysql")) { - return mysqlVersion(majorVersion, minorVersion); } else if (dbProductName.contains("h2")) { return new H2Platform(); } else if (dbProductName.contains("hsql database engine")) { return new HsqldbPlatform(); } else if (dbProductName.contains("postgres")) { return readPostgres(connection, majorVersion); + } else if (dbProductName.contains("mariadb")) { + return new MariaDbPlatform(); + } else if (dbProductName.contains("mysql")) { + return mysqlVersion(majorVersion, minorVersion); } else if (dbProductName.contains("nuo")) { return new NuoDbPlatform(); } else if (dbProductName.contains("sqlite")) { diff --git a/src/main/java/io/ebeaninternal/server/core/InternalConfiguration.java b/src/main/java/io/ebeaninternal/server/core/InternalConfiguration.java index 033841ab6..5fa296997 100644 --- a/src/main/java/io/ebeaninternal/server/core/InternalConfiguration.java +++ b/src/main/java/io/ebeaninternal/server/core/InternalConfiguration.java @@ -503,10 +503,9 @@ public class InternalConfiguration { * Create the DataTimeZone implementation to use. */ private DataTimeZone initDataTimeZone() { - String tz = serverConfig.getDataTimeZone(); if (tz == null) { - if (isMySql(getPlatform())) { + if (isMySqlOrMariaDB(getPlatform())) { return new MySqlDataTimeZone(); } return new NoDataTimeZone(); @@ -518,8 +517,8 @@ public class InternalConfiguration { } } - private boolean isMySql(Platform platform) { - return platform.base() == Platform.MYSQL; + private boolean isMySqlOrMariaDB(Platform platform) { + return platform.base() == Platform.MYSQL || platform.base() == Platform.MARIADB; } public DataTimeZone getDataTimeZone() { diff --git a/src/main/java/io/ebeaninternal/server/core/PlatformDdlBuilder.java b/src/main/java/io/ebeaninternal/server/core/PlatformDdlBuilder.java index 1c39dbbb8..7e6701bc0 100644 --- a/src/main/java/io/ebeaninternal/server/core/PlatformDdlBuilder.java +++ b/src/main/java/io/ebeaninternal/server/core/PlatformDdlBuilder.java @@ -7,6 +7,7 @@ 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; @@ -31,6 +32,8 @@ public class PlatformDdlBuilder { return new H2Ddl(platform); case DB2: return new DB2Ddl(platform); + case MARIADB: + return new MariaDbDdl(platform); case MYSQL55: case MYSQL: return new MySqlDdl(platform); diff --git a/src/main/java/io/ebeaninternal/server/expression/platform/DbExpressionHandlerFactory.java b/src/main/java/io/ebeaninternal/server/expression/platform/DbExpressionHandlerFactory.java index 2a99dcab4..c4b24fc84 100644 --- a/src/main/java/io/ebeaninternal/server/expression/platform/DbExpressionHandlerFactory.java +++ b/src/main/java/io/ebeaninternal/server/expression/platform/DbExpressionHandlerFactory.java @@ -18,6 +18,8 @@ public class DbExpressionHandlerFactory { case POSTGRES: case POSTGRES9: return new PostgresDbExpression(); + case MARIADB: + return new MariaDbExpression(); case MYSQL55: case MYSQL: return new MySqlDbExpression(); diff --git a/src/main/java/io/ebeaninternal/server/expression/platform/MariaDbExpression.java b/src/main/java/io/ebeaninternal/server/expression/platform/MariaDbExpression.java new file mode 100644 index 000000000..8e4c0be1d --- /dev/null +++ b/src/main/java/io/ebeaninternal/server/expression/platform/MariaDbExpression.java @@ -0,0 +1,16 @@ +package io.ebeaninternal.server.expression.platform; + +import io.ebeaninternal.api.SpiExpressionRequest; +import io.ebeaninternal.server.expression.Op; + +/** + * MariaDB specific handling of platform specific expressions. + */ +class MariaDbExpression extends BasicDbExpression { + + @Override + public void json(SpiExpressionRequest request, String propName, String path, Op operator, Object value) { + request.append("(").append(propName).append(" ->> '$.").append(path).append("')"); + request.append(operator.bind()); + } +} diff --git a/src/main/java/io/ebeaninternal/server/persist/dml/InsertMeta.java b/src/main/java/io/ebeaninternal/server/persist/dml/InsertMeta.java index 13182705a..abaefd095 100644 --- a/src/main/java/io/ebeaninternal/server/persist/dml/InsertMeta.java +++ b/src/main/java/io/ebeaninternal/server/persist/dml/InsertMeta.java @@ -13,6 +13,9 @@ import io.ebeaninternal.server.persist.dmlbind.BindableList; import java.sql.SQLException; +import static io.ebean.annotation.Platform.MARIADB; +import static io.ebean.annotation.Platform.MYSQL; + /** * Meta data for insert handler. The meta data is for a particular bean type. It * is considered immutable and is thread safe. @@ -166,12 +169,7 @@ final class InsertMeta { request.append("insert into ").append(table); if (nullId && noColumnsForInsert(draftTable)) { - if (this.platform.base() == Platform.MYSQL) { - request.append(" values (default)"); - } else { - request.append(" default values"); - } - return request.toString(); + return request.append(defaultValues()).toString(); } request.append(" ("); @@ -199,6 +197,10 @@ final class InsertMeta { return request.toString(); } + private String defaultValues() { + return platform.base() == MYSQL || platform.base() == MARIADB ? " values (default)" : " default values"; + } + /** * Return true if the insert actually contains no columns. */ diff --git a/src/test/java/io/ebean/BaseTestCase.java b/src/test/java/io/ebean/BaseTestCase.java index 6c1326d77..df0d732ed 100644 --- a/src/test/java/io/ebean/BaseTestCase.java +++ b/src/test/java/io/ebean/BaseTestCase.java @@ -206,6 +206,10 @@ public abstract class BaseTestCase { return Platform.MYSQL == platform(); } + public boolean isMariaDB() { + return Platform.MARIADB == platform(); + } + public boolean isHana() { return Platform.HANA == platform(); } diff --git a/src/test/java/io/ebean/UpdateQueryTest.java b/src/test/java/io/ebean/UpdateQueryTest.java index 9096c20e1..43ca6d7f7 100644 --- a/src/test/java/io/ebean/UpdateQueryTest.java +++ b/src/test/java/io/ebean/UpdateQueryTest.java @@ -168,7 +168,7 @@ public class UpdateQueryTest extends BaseTestCase { assertSql(query).contains("update o_customer cust set status=?, updtime=? where id > ?"); } - @IgnorePlatform(Platform.MYSQL) + @IgnorePlatform({Platform.MYSQL, Platform.MARIADB}) @Test public void withJoin() { @@ -214,7 +214,7 @@ public class UpdateQueryTest extends BaseTestCase { assertSql(sql.get(0)).contains("update o_customer set status=? where id in (select t0.id from o_customer t0 left join o_address t1 on t1.id = t0.billing_address_id where t1.country_code = ? and t0.id > ? limit 100)"); } - @ForPlatform({Platform.H2, Platform.POSTGRES, Platform.MYSQL}) + @ForPlatform({Platform.H2, Platform.POSTGRES, Platform.MYSQL, Platform.MARIADB}) @Test public void simpleWithLimit() { @@ -230,7 +230,7 @@ public class UpdateQueryTest extends BaseTestCase { .update(); final List sql = LoggedSqlCollector.stop(); - if (isMySql() || isH2()) { + if (isMySql() || isH2() || isMariaDB()) { assertSql(sql.get(0)).contains("update o_customer set status=? where id > ? limit 100"); } else { assertSql(sql.get(0)).contains("update o_customer set status=? where id in (select t0.id from o_customer t0 where t0.id > ? limit 100)"); diff --git a/src/test/java/io/ebeaninternal/dbmigration/DbMigrationTest.java b/src/test/java/io/ebeaninternal/dbmigration/DbMigrationTest.java index 53c477266..e16414594 100644 --- a/src/test/java/io/ebeaninternal/dbmigration/DbMigrationTest.java +++ b/src/test/java/io/ebeaninternal/dbmigration/DbMigrationTest.java @@ -24,7 +24,7 @@ public class DbMigrationTest extends BaseTestCase { server().script().runScript(scriptName, content, useAutoCommit); } - @IgnorePlatform({Platform.ORACLE, Platform.NUODB}) + @IgnorePlatform({Platform.ORACLE, Platform.NUODB, Platform.MARIADB}) @Test public void testRunMigration() throws IOException { // first clean up previously created objects diff --git a/src/test/java/main/StartMariaDb.java b/src/test/java/main/StartMariaDb.java new file mode 100644 index 000000000..8bdf82bd7 --- /dev/null +++ b/src/test/java/main/StartMariaDb.java @@ -0,0 +1,18 @@ +package main; + +import io.ebean.docker.commands.MariaDBConfig; +import io.ebean.docker.commands.MariaDBContainer; + +public class StartMariaDb { + + public static void main(String[] args) { + + MariaDBConfig config = new MariaDBConfig("10.5"); + config.setDbName("unit"); + config.setUser("unit"); + config.setPassword("unit"); + + MariaDBContainer container = new MariaDBContainer(config); + container.startWithDropCreate(); + } +} diff --git a/src/test/java/org/tests/aggregateformula/TestAggregateFormula.java b/src/test/java/org/tests/aggregateformula/TestAggregateFormula.java index f646ff9e8..b9dc68a03 100644 --- a/src/test/java/org/tests/aggregateformula/TestAggregateFormula.java +++ b/src/test/java/org/tests/aggregateformula/TestAggregateFormula.java @@ -19,7 +19,7 @@ import static org.junit.Assert.assertNotNull; public class TestAggregateFormula extends BaseTestCase { - @IgnorePlatform({Platform.SQLSERVER, Platform.MYSQL, Platform.NUODB}) + @IgnorePlatform({Platform.SQLSERVER, Platform.MYSQL, Platform.MARIADB, Platform.NUODB}) @Test public void minDistinctOrderByNulls() { diff --git a/src/test/java/org/tests/basic/TestMetaAnnotation.java b/src/test/java/org/tests/basic/TestMetaAnnotation.java index 3b4c781f6..f4638884e 100644 --- a/src/test/java/org/tests/basic/TestMetaAnnotation.java +++ b/src/test/java/org/tests/basic/TestMetaAnnotation.java @@ -49,7 +49,7 @@ public class TestMetaAnnotation extends BaseTestCase { * This test writes 101 spaces to "line1" which is annotated with @Size(max=100). */ @Test - @IgnorePlatform({Platform.POSTGRES, Platform.SQLSERVER, Platform.MYSQL}) // pg & mssql does not fail if string is too long. + @IgnorePlatform({Platform.POSTGRES, Platform.SQLSERVER, Platform.MYSQL, Platform.MARIADB}) // pg & mssql does not fail if string is too long. public void testWrite101SpacesToLine1() { Address address = new Address(); @@ -66,7 +66,7 @@ public class TestMetaAnnotation extends BaseTestCase { * This test writes 101 spaces to "line1" which is meta-annotated with {@link SizeMedium}. */ @Test - @IgnorePlatform({Platform.POSTGRES, Platform.SQLSERVER, Platform.MYSQL}) + @IgnorePlatform({Platform.POSTGRES, Platform.SQLSERVER, Platform.MYSQL, Platform.MARIADB}) public void testWrite101SpacesToLine2() { Address address = new Address(); diff --git a/src/test/java/org/tests/basic/TestQueryForUpdate.java b/src/test/java/org/tests/basic/TestQueryForUpdate.java index 2f4800a82..45c5d5b9f 100644 --- a/src/test/java/org/tests/basic/TestQueryForUpdate.java +++ b/src/test/java/org/tests/basic/TestQueryForUpdate.java @@ -23,7 +23,7 @@ public class TestQueryForUpdate extends BaseTestCase { @Test - @ForPlatform({Platform.H2, Platform.ORACLE, Platform.POSTGRES, Platform.SQLSERVER, Platform.MYSQL}) + @ForPlatform({Platform.H2, Platform.ORACLE, Platform.POSTGRES, Platform.SQLSERVER, Platform.MYSQL, Platform.MARIADB}) public void testForUpdate() { ResetBasicData.reset(); @@ -41,7 +41,7 @@ public class TestQueryForUpdate extends BaseTestCase { } @Test - @ForPlatform({ Platform.H2, Platform.ORACLE, Platform.POSTGRES, Platform.SQLSERVER, Platform.MYSQL}) + @ForPlatform({ Platform.H2, Platform.ORACLE, Platform.POSTGRES, Platform.SQLSERVER, Platform.MYSQL, Platform.MARIADB}) public void testForUpdate_when_alreadyInPC() { EBasic basic = new EBasic("test PC cache"); diff --git a/src/test/java/org/tests/defaultvalues/TestDefaults.java b/src/test/java/org/tests/defaultvalues/TestDefaults.java index b2bc6e2d1..a5f7affb2 100644 --- a/src/test/java/org/tests/defaultvalues/TestDefaults.java +++ b/src/test/java/org/tests/defaultvalues/TestDefaults.java @@ -26,7 +26,7 @@ public class TestDefaults extends BaseTestCase { final List current = LoggedSqlCollector.current(); assertThat(current).isNotEmpty(); - if (isMySql()) { + if (isMySql() || isMariaDB()) { assertThat(current.get(0)).contains("insert into defaults_model_draft values (default);"); } else if (isSqlServer()) { assertThat(current.get(0)).contains("insert into defaults_model_draft (id) values (?)"); diff --git a/src/test/java/org/tests/delete/TestDeleteByQuery.java b/src/test/java/org/tests/delete/TestDeleteByQuery.java index 52fc8eb73..9aceaacb1 100644 --- a/src/test/java/org/tests/delete/TestDeleteByQuery.java +++ b/src/test/java/org/tests/delete/TestDeleteByQuery.java @@ -27,7 +27,7 @@ public class TestDeleteByQuery extends BaseTestCase { } @Test - @IgnorePlatform(Platform.MYSQL) + @IgnorePlatform({Platform.MYSQL, Platform.MARIADB}) public void deleteWithLimit() { createUser("deleteWithLimit1"); createUser("deleteWithLimit2"); diff --git a/src/test/java/org/tests/query/orderby/TestOrderByWithDistinct.java b/src/test/java/org/tests/query/orderby/TestOrderByWithDistinct.java index 72a2bd691..cf451eee2 100644 --- a/src/test/java/org/tests/query/orderby/TestOrderByWithDistinct.java +++ b/src/test/java/org/tests/query/orderby/TestOrderByWithDistinct.java @@ -41,7 +41,7 @@ public class TestOrderByWithDistinct extends BaseTestCase { } @Test - @IgnorePlatform({Platform.MYSQL, Platform.SQLSERVER, Platform.NUODB}) // do not support nulls first/last + @IgnorePlatform({Platform.MYSQL, Platform.MARIADB, Platform.SQLSERVER, Platform.NUODB}) // do not support nulls first/last public void testDistinctOn() { MRole role = Ebean.getReference(MRole.class, 1); diff --git a/src/test/java/org/tests/query/other/TestWhereLikeWithSlash.java b/src/test/java/org/tests/query/other/TestWhereLikeWithSlash.java index 71dfe5600..c36cd28c8 100644 --- a/src/test/java/org/tests/query/other/TestWhereLikeWithSlash.java +++ b/src/test/java/org/tests/query/other/TestWhereLikeWithSlash.java @@ -26,7 +26,7 @@ public class TestWhereLikeWithSlash extends BaseTestCase { Query query1 = Ebean.find(EBasic.class).where().like("name", "slash\\mon%").query(); List list1 = query1.findList(); - if (!isMySql() && !isNuoDb()) { + if (!isMySql() && !isNuoDb() && !isMariaDB()) { // For mysql this assert depends on no_backslash_escapes setting so we won't assert here // Still good on Postgres which was the original issue assertEquals(1, list1.size()); diff --git a/src/test/java/org/tests/rawsql/TestRawSqlOrmQuery.java b/src/test/java/org/tests/rawsql/TestRawSqlOrmQuery.java index 58b8c2285..e29798c6b 100644 --- a/src/test/java/org/tests/rawsql/TestRawSqlOrmQuery.java +++ b/src/test/java/org/tests/rawsql/TestRawSqlOrmQuery.java @@ -117,7 +117,7 @@ public class TestRawSqlOrmQuery extends BaseTestCase { } @Test - @IgnorePlatform({Platform.MYSQL, Platform.SQLSERVER}) + @IgnorePlatform({Platform.MYSQL, Platform.MARIADB, Platform.SQLSERVER}) public void test_upperCaseSql() { ResetBasicData.reset(); diff --git a/src/test/java/org/tests/types/TestNewTypes.java b/src/test/java/org/tests/types/TestNewTypes.java index e8a75a4de..ca02e91bc 100644 --- a/src/test/java/org/tests/types/TestNewTypes.java +++ b/src/test/java/org/tests/types/TestNewTypes.java @@ -36,6 +36,9 @@ public class TestNewTypes extends BaseTestCase { @Test public void testInsertUpdate() throws InterruptedException { + + DB.find(SomeNewTypesBean.class).delete(); + SomeNewTypesBean bean = new SomeNewTypesBean(); bean.setLocalDate(LocalDate.now()); bean.setLocalDateTime(LocalDateTime.now()); @@ -65,55 +68,55 @@ public class TestNewTypes extends BaseTestCase { DB.save(bean); Thread.sleep(DB_CLOCK_DELTA); // wait, to ensure that instant < Instant.now() List list = DB.find(SomeNewTypesBean.class).where().lt("instant", Instant.now()).findList(); - assertTrue(!list.isEmpty()); + assertThat(list).isNotEmpty(); list = DB.find(SomeNewTypesBean.class).where().le("localDate", LocalDate.now()).findList(); - assertTrue(!list.isEmpty()); + assertThat(list).isNotEmpty(); list = DB.find(SomeNewTypesBean.class).where().lt("localDateTime", LocalDateTime.now()).findList(); - assertTrue(!list.isEmpty()); + assertThat(list).isNotEmpty(); list = DB.find(SomeNewTypesBean.class).where().lt("offsetDateTime", OffsetDateTime.now()).findList(); - assertTrue(!list.isEmpty()); + assertThat(list).isNotEmpty(); list = DB.find(SomeNewTypesBean.class).where().lt("zonedDateTime", ZonedDateTime.now()).findList(); - assertTrue(!list.isEmpty()); + assertThat(list).isNotEmpty(); list = DB.find(SomeNewTypesBean.class).where().eq("sqlDate", bean.getSqlDate()).findList(); - assertTrue(!list.isEmpty()); + assertThat(list).isNotEmpty(); list = DB.find(SomeNewTypesBean.class).where().eq("sqlTime", bean.getSqlTime()).findList(); - assertTrue(!list.isEmpty()); + assertThat(list).isNotEmpty(); list = DB.find(SomeNewTypesBean.class).where().le("localTime", LocalTime.now()).findList(); - assertTrue(!list.isEmpty()); + assertThat(list).isNotEmpty(); list = DB.find(SomeNewTypesBean.class).where().eq("zoneId", ZoneId.systemDefault().getId()).findList(); - assertTrue(!list.isEmpty()); + assertThat(list).isNotEmpty(); list = DB.find(SomeNewTypesBean.class).where().eq("zoneOffset", ZonedDateTime.now().getOffset()).findList(); - assertTrue(!list.isEmpty()); + assertThat(list).isNotEmpty(); list = DB.find(SomeNewTypesBean.class).where().le("yearMonth", YearMonth.of(2014, 9)).findList(); - assertTrue(!list.isEmpty()); + assertThat(list).isNotEmpty(); list = DB.find(SomeNewTypesBean.class).where().le("monthDay", MonthDay.of(9,22)).findList(); - assertTrue(!list.isEmpty()); + assertThat(list).isNotEmpty(); list = DB.find(SomeNewTypesBean.class).where().le("year", Year.now()).findList(); - assertTrue(!list.isEmpty()); + assertThat(list).isNotEmpty(); list = DB.find(SomeNewTypesBean.class).where().le("month", Month.SEPTEMBER).findList(); - assertTrue(!list.isEmpty()); + assertThat(list).isNotEmpty(); list = DB.find(SomeNewTypesBean.class).where().eq("path", Paths.get(TEMP_PATH)).findList(); - assertTrue(!list.isEmpty()); + assertThat(list).isNotEmpty(); list = DB.find(SomeNewTypesBean.class).where().eq("period", Period.of(4,3,2)).findList(); - assertTrue(!list.isEmpty()); + assertThat(list).isNotEmpty(); list = DB.find(SomeNewTypesBean.class).where().eq("duration", Duration.ofMinutes(5)).findList(); - assertTrue(!list.isEmpty()); + assertThat(list).isNotEmpty(); SomeNewTypesBean fetched = DB.find(SomeNewTypesBean.class, bean.getId()); diff --git a/src/test/resources/ebean.properties b/src/test/resources/ebean.properties index 7fcde144e..7c6ce5d89 100644 --- a/src/test/resources/ebean.properties +++ b/src/test/resources/ebean.properties @@ -22,7 +22,7 @@ ebean.ddl.generate=true ebean.ddl.run=true ebean.ddl.header=-- Generated by ebean ${version} at ${timestamp} ebean.packages=org.tests -datasource.default=h2 +datasource.default=mariadb #datasource.h2.capturestacktrace=true ebean.dumpMetricsOnShutdown=true @@ -135,6 +135,10 @@ datasource.mysql.url=jdbc:mysql://127.0.0.1:4306/unit #datasource.mysql.url=jdbc:mysql://127.0.0.1:4306/test_ebean #datasource.mysql.driver=com.mysql.cj.jdbc.Driver +datasource.mariadb.username=unit +datasource.mariadb.password=unit +datasource.mariadb.url=jdbc:mariadb://localhost:4306/unit + datasource.cockroach.username=root datasource.cockroach.password= datasource.cockroach.url=jdbc:postgresql://localhost:26257/unit?sslmode=disable