Compare commits

..
Author SHA1 Message Date
rob bygrave 3f12739e71 [maven-release-plugin] prepare release ebean-11.21.1 2018-08-13 22:06:26 +12:00
rob bygrave cd1ae20015 Bump to 11.21.1-SNAPSHOT 2018-08-13 22:00:15 +12:00
rob bygrave 6d1e3a08e1 #1470 - Change OnQueryOnly to default to OnQueryOnly.COMMIT 2018-08-13 21:59:33 +12:00
rob bygrave 94e6fe90e5 No effective change - white space format 2018-08-13 21:54:19 +12:00
SnōwballandRob Bygrave 9bbe99657a Let enum arrays be saved as integers or strings (#1467)
* Let enums be saved as integers or strings as needed

* Add tests for int and varchar @DbArray enums
2018-08-13 21:26:14 +12:00
rob bygrave 0faf9f54ea [maven-release-plugin] prepare for next development iteration 2018-08-10 15:54:42 +12:00
rob bygrave 046a9b30b0 [maven-release-plugin] prepare release ebean-11.20.2 2018-08-10 15:54:29 +12:00
Rob BygraveandGitHub afb7679302 #1468 - ENH: Add built-in DDL generation for Postgres table partitioning (by day, week, month and year) (#1469) 2018-08-10 14:52:42 +12:00
rob bygrave db35f0f5ed #1466 - With ebean.dbSchema property (aka default db schema) we should not connection.setSchema() when running migrations 2018-08-09 21:34:23 +12:00
rob bygrave 19756421ad [maven-release-plugin] prepare for next development iteration 2018-08-08 23:34:11 +12:00
rob bygrave 1d678576f7 [maven-release-plugin] prepare release ebean-11.20.1 2018-08-08 23:33:57 +12:00
Rob BygraveandGitHub ded7d44974 Add support for Postgres range partition DDL plus refactor xsd definitions that had got out of sync (#1465)
* Add partition meta mapping

* Fix to allow schema prefix on select dynamic formula

* Add partition meta mapping

* Migration XSD Update

* Fix to allow schema prefix on select dynamic formula

* Bump ebean-migration to 11.8.1
2018-08-08 23:29:29 +12:00
rob bygrave c9497fd654 [maven-release-plugin] prepare for next development iteration 2018-08-08 12:06:03 +12:00
rob bygrave 23a9cf9bb3 [maven-release-plugin] prepare release ebean-11.19.3 2018-08-08 12:05:50 +12:00
rob bygrave 75395a8481 #1464 - NPE in PropertiesWrapper.internalGet(PropertiesWrapper.java:74) running multi-platform DB migration generation with ServerConfig that has no properties
Note that newPlatformConfig() is only used for multi-platform DB migration generation.
2018-08-07 11:55:06 +12:00
rob bygrave a161d0c01b #1463 - NPE in MigrationModel.readMigrations() when running using File resources with no migration directory 2018-08-07 11:36:26 +12:00
rob bygrave 1dd7720605 #1462 - DB Migration generation with Postgis types gives us non-strict geometry column DDL 2018-08-07 11:19:09 +12:00
rob bygrave 3d2f885e87 [maven-release-plugin] prepare for next development iteration 2018-08-06 21:42:56 +12:00
rob bygrave 2b3e485beb [maven-release-plugin] prepare release ebean-11.19.2 2018-08-06 21:42:41 +12:00
rob bygrave d4be0718a8 #1460 - ENH: Add FetchGroup ... as a mechanism to specify select() + fetch() separately from Query
Change to add generics for FetchGroup
2018-08-06 21:30:36 +12:00
rob bygrave 6d0556a120 #1460 - ENH: Add FetchGroup ... as a mechanism to specify select() + fetch() separately from Query
Change to add generics for FetchGroup
2018-08-02 23:18:44 +12:00
rob bygrave ec7e4e4935 #1461 - @OneToMany with @JoinTable without table name does not default join table name 2018-08-02 22:25:48 +12:00
rob bygrave 471605619a #1460 - ENH: Add FetchGroup ... as a mechanism to specify select() + fetch() separately from Query 2018-07-30 23:51:52 +12:00
rob bygrave 02fe956056 #1459 - findNative with join to a @OneToMany ... does not map properly 2018-07-27 12:31:56 +12:00
rob bygrave db13034fec No effective change - update code style in test 2018-07-27 12:31:27 +12:00
rob bygrave c2489beb82 [maven-release-plugin] prepare for next development iteration 2018-07-18 22:25:00 +12:00
90 changed files with 2039 additions and 361 deletions
+4 -4
View File
@@ -9,7 +9,7 @@
<groupId>io.ebean</groupId>
<artifactId>ebean</artifactId>
<version>11.19.1</version>
<version>11.21.1</version>
<packaging>jar</packaging>
<name>ebean</name>
@@ -22,7 +22,7 @@
<scm>
<developerConnection>scm:git:git@github.com:ebean-orm/ebean.git</developerConnection>
<tag>ebean-11.19.1</tag>
<tag>ebean-11.21.1</tag>
</scm>
<profiles>
@@ -117,7 +117,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-annotation</artifactId>
<version>4.1</version>
<version>4.2</version>
</dependency>
<dependency>
@@ -135,7 +135,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-migration</artifactId>
<version>11.7.1</version>
<version>11.8.2</version>
</dependency>
<dependency>
@@ -412,6 +412,11 @@ public interface ExpressionList<T> {
*/
Query<T> select(String properties);
/**
* Apply the fetchGroup which defines what part of the object graph to load.
*/
Query<T> select(FetchGroup<T> fetchGroup);
/**
* Set whether this query uses DISTINCT.
* <p>
+114
View File
@@ -0,0 +1,114 @@
package io.ebean;
import javax.annotation.Nonnull;
/**
* Defines what part of the object graph to load (select and fetch clauses).
* <p>
* Using a FetchGroup effectively sets the select() and fetch() clauses for a query. It is alternative
* to specifying the select() and fetch() clauses on the query allowing for more re-use of "what to load"
* that can be defined separately from the query and combined with other FetchGroups.
* </p>
*
* <h3>Select example</h3>*
* <pre>{@code
*
* FetchGroup<Customer> fetchGroup = FetchGroup.of(Customer.class, "name, status");
*
* Customer.query()
* .select(fetchGroup)
* .findList();
*
* }</pre>
*
* <h3>Select and fetch example</h3>
* <pre>{@code
*
* FetchGroup<Customer> fetchGroup = FetchGroup.of(Customer.class)
* .select("name, status")
* .fetch("contacts", "firstName, lastName, email")
* .build();
*
* Customer.query()
* .select(fetchGroup)
* .findList();
*
* }</pre>
*
* <h3>Combining FetchGroups</h3>
* <p>
* FetchGroups can be combined together to form another FetchGroup.
* </p>
* <pre>{@code
*
* FetchGroup<Address> FG_ADDRESS = FetchGroup.of(Address.class)
* .select("line1, line2, city")
* .fetch("country", "name")
* .build();
*
* FetchGroup<Customer> FG_CUSTOMER = FetchGroup.of(Customer.class)
* .select("name, version")
* .fetch("billingAddress", FG_ADDRESS)
* .build();
*
*
* Customer.query()
* .select(FG_CUSTOMER)
* .findList();
*
* }</pre>
*
* @param <T> The bean type the Fetch group can be applied to
*/
public interface FetchGroup<T> {
/**
* Return the FetchGroup with the given select clause.
* <p>
* We use this for simple FetchGroup that only select() properties and do not have additional fetch() clause.
* </p>
* <pre>{@code
*
* FetchGroup<Customer> fetchGroup = FetchGroup.of(Customer.class, "name, status");
*
* Customer.query()
* .select(fetchGroup)
* .findList();
*
* }</pre>
*
* @param select The select clause of the FetchGroup
*
* @return The FetchGroup with the given select clause
*/
@Nonnull
static <T> FetchGroup<T> of(Class<T> cls, String select) {
return XServiceProvider.fetchGroupOf(cls, select);
}
/**
* Return the FetchGroupBuilder with the given select clause that we can add fetch clauses to.
* <p>
* We chain select() with one or more fetch() clauses to define the object graph to load.
* </p>
* <pre>{@code
*
* FetchGroup<Customer> fetchGroup = FetchGroup.of(Customer.class)
* .select("name, status")
* .fetch("contacts", "firstName, lastName, email")
* .build();
*
* Customer.query()
* .select(fetchGroup)
* .findList();
*
* }</pre>
*
* @return The FetchGroupBuilder with the given select clause which we will add fetch clauses to
*/
@Nonnull
static <T> FetchGroupBuilder<T> of(Class<T> cls) {
return XServiceProvider.fetchGroupOf(cls);
}
}
@@ -0,0 +1,93 @@
package io.ebean;
import javax.annotation.Nonnull;
/**
* Builds a FetchGroup by adding fetch clauses.
* <p>
* We add select() and fetch() clauses to define the object graph we want to load.
* </p>
*
* <pre>{@code
*
* FetchGroup fetchGroup = FetchGroup
* .select("name, status")
* .fetch("contacts", "firstName, lastName, email")
* .build();
*
* Customer.query()
* .select(fetchGroup)
* .where()
* ...
* .findList();
*
* }</pre>
*/
public interface FetchGroupBuilder<T> {
/**
* Specify specific properties to select (top level properties).
*/
@Nonnull
FetchGroupBuilder<T> select(String select);
/**
* Fetch all the properties at the given path.
*/
@Nonnull
FetchGroupBuilder<T> fetch(String path);
/**
* Fetch the path with the nested fetch group.
*/
@Nonnull
FetchGroupBuilder<T> fetch(String path, FetchGroup<?> nestedGroup);
/**
* Fetch the path using a query join with the nested fetch group.
*/
@Nonnull
FetchGroupBuilder<T> fetchQuery(String path, FetchGroup<?> nestedGroup);
/**
* Fetch the path lazily with the nested fetch group.
*/
@Nonnull
FetchGroupBuilder<T> fetchLazy(String path, FetchGroup<?> nestedGroup);
/**
* Fetch the path including specified properties.
*/
@Nonnull
FetchGroupBuilder<T> fetch(String path, String properties);
/**
* Fetch the path including all its properties using a query join.
*/
@Nonnull
FetchGroupBuilder<T> fetchQuery(String path);
/**
* Fetch the path including specified properties using a query join.
*/
@Nonnull
FetchGroupBuilder<T> fetchQuery(String path, String properties);
/**
* Fetch the path including all its properties lazily.
*/
@Nonnull
FetchGroupBuilder<T> fetchLazy(String path);
/**
* Fetch the path including specified properties lazily.
*/
@Nonnull
FetchGroupBuilder<T> fetchLazy(String path, String properties);
/**
* Build and return the FetchGroup.
*/
@Nonnull
FetchGroup<T> build();
}
+5
View File
@@ -397,6 +397,11 @@ public interface Query<T> {
*/
Query<T> select(String fetchProperties);
/**
* Apply the fetchGroup which defines what part of the object graph to load.
*/
Query<T> select(FetchGroup<T> fetchGroup);
/**
* Specify a path to fetch eagerly including specific properties.
* <p>
@@ -1,5 +1,6 @@
package io.ebean;
import io.ebean.service.SpiFetchGroupService;
import io.ebean.service.SpiProfileLocationFactory;
import io.ebean.service.SpiRawSqlService;
@@ -15,6 +16,16 @@ class XServiceProvider {
private static SpiProfileLocationFactory profileLocationFactory = initProfileLocation();
private static SpiFetchGroupService fetchGroupService = initSpiFetchGroupService();
private static SpiFetchGroupService initSpiFetchGroupService() {
Iterator<SpiFetchGroupService> loader = ServiceLoader.load(SpiFetchGroupService.class).iterator();
if (loader.hasNext()) {
return loader.next();
}
throw new IllegalStateException("No service implementation found for SpiFetchGroupService?");
}
private static SpiRawSqlService initRawSql() {
Iterator<SpiRawSqlService> loader = ServiceLoader.load(SpiRawSqlService.class).iterator();
@@ -47,4 +58,17 @@ class XServiceProvider {
return profileLocationFactory;
}
/**
* Return the FetchGroup with the given select clause.
*/
static <T> FetchGroup<T> fetchGroupOf(Class<T> cls, String select) {
return fetchGroupService.of(cls, select);
}
/**
* Return the FetchGroupBuilder with the given select clause.
*/
static <T> FetchGroupBuilder<T> fetchGroupOf(Class<T> cls) {
return fetchGroupService.of(cls);
}
}
@@ -107,6 +107,11 @@ public class DbMigrationConfig {
*/
protected String dbSchema;
/**
* Set to true if we consider this the 'default schema' (Postgres schema that matches DB username)
*/
protected boolean defaultDbSchema;
/**
* DB user used to run the DB migration.
*/
@@ -400,9 +405,15 @@ public class DbMigrationConfig {
* Set the Db schema if it hasn't already been defined.
*/
public void setDefaultDbSchema(String dbSchema) {
if (this.dbSchema == null) {
this.dbSchema = dbSchema;
}
this.defaultDbSchema = true;
this.dbSchema = dbSchema;
}
/**
* Return true if this is considered the default DB schema (Postgres schema matching DB username).
*/
public boolean isDefaultDbSchema() {
return defaultDbSchema;
}
/**
@@ -591,6 +602,9 @@ public class DbMigrationConfig {
runnerConfig.setDbUsername(getDbUsername());
runnerConfig.setDbPassword(getDbPassword());
runnerConfig.setDbSchema(getDbSchema());
if (defaultDbSchema) {
runnerConfig.setSetCurrentSchema(false);
}
runnerConfig.setClassLoader(classLoader);
if (patchInsertOn != null) {
runnerConfig.setPatchInsertOn(patchInsertOn);
@@ -3130,9 +3130,10 @@ public class ServerConfig {
* @return A copy of the PlatformConfig with overridden properties
*/
public PlatformConfig newPlatformConfig(String propertiesPath, String platformPrefix) {
if (properties == null) {
properties = new Properties();
}
PropertiesWrapper p = new PropertiesWrapper(propertiesPath, platformPrefix, properties, classLoadConfig);
PlatformConfig config = new PlatformConfig(platformConfig);
config.loadSettings(p);
return config;
@@ -2,6 +2,7 @@ package io.ebean.config.dbplatform;
import io.ebean.BackgroundExecutor;
import io.ebean.Query;
import io.ebean.annotation.PartitionMode;
import io.ebean.annotation.PersistBatch;
import io.ebean.annotation.Platform;
import io.ebean.config.CustomDbTypeMapping;
@@ -51,7 +52,7 @@ public class DatabasePlatform {
/**
* The behaviour used when ending a read only transaction at read committed isolation level.
*/
protected OnQueryOnly onQueryOnly = OnQueryOnly.ROLLBACK;
protected OnQueryOnly onQueryOnly = OnQueryOnly.COMMIT;
/**
* The open quote used by quoted identifiers.
@@ -706,6 +707,20 @@ public class DatabasePlatform {
}
}
/**
* Return true if partitions exist for the given table.
*/
public boolean tablePartitionsExist(Connection connection, String table) throws SQLException {
return true;
}
/**
* Return the SQL to create an initial partition for the given table.
*/
public String tablePartitionInit(String tableName, PartitionMode mode, String property, String singlePrimaryKey) {
return null;
}
/**
* Escapes the like string for this DB-Platform
*/
@@ -2,6 +2,7 @@ package io.ebean.config.dbplatform.postgres;
import io.ebean.BackgroundExecutor;
import io.ebean.Query;
import io.ebean.annotation.PartitionMode;
import io.ebean.annotation.Platform;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.config.dbplatform.DbPlatformType;
@@ -11,6 +12,10 @@ import io.ebean.config.dbplatform.PlatformIdGenerator;
import io.ebean.config.dbplatform.SqlErrorCodes;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
/**
@@ -116,4 +121,31 @@ public class PostgresPlatform extends DatabasePlatform {
return sql + " for update";
}
}
@Override
public boolean tablePartitionsExist(Connection connection, String table) throws SQLException {
try (PreparedStatement statement = connection.prepareStatement("select count(*) from pg_inherits i WHERE i.inhparent = ?::regclass")) {
statement.setString(1, table);
try (ResultSet resultSet = statement.executeQuery()) {
return resultSet.next() && resultSet.getInt(1) > 0;
}
}
}
/**
* Return SQL using built in partition helper functions to create some initial partitions.
*
* Only use this if extra-dll doesn't have some initial partitions defined (which it should).
*/
public String tablePartitionInit(String tableName, PartitionMode mode, String property, String pkey) {
if (property == null) {
property = "";
}
if (pkey == null) {
pkey = "";
}
return "select partition('" + mode.name().toLowerCase() + "','" + tableName + "','" + pkey + "','" + property + "',1);";
}
}
@@ -98,6 +98,11 @@ public interface DbMigration {
*/
void setIncludeGeneratedFileComment(boolean includeGeneratedFileComment);
/**
* Set this to false to exclude the builtin support for table partitioning (with @DbPartition).
*/
void setIncludeBuiltInPartitioning(boolean includeBuiltInPartitioning);
/**
* Set the header that is included in the generated DDL script.
*/
@@ -0,0 +1,26 @@
package io.ebean.service;
import io.ebean.FetchGroup;
import io.ebean.FetchGroupBuilder;
/**
* Service that parses FetchGroup expressions.
*/
public interface SpiFetchGroupService {
/**
* Return the FetchGroup with the given select clause.
*
* @param beanType The type of entity bean the fetch group is for
* @param select The properties to select (top level properties)
*/
<T> FetchGroup<T> of(Class<T> beanType, String select);
/**
* Create and return a FetchGroupBuilder starting with a select() clause.
*
* @param beanType The type of entity bean the fetch group is for
* @return The FetchGroupBuilder to add additional select and fetch clauses
*/
<T> FetchGroupBuilder<T> of(Class<T> beanType);
}
@@ -1,11 +1,14 @@
package io.ebeaninternal.dbmigration;
import io.ebean.config.ServerConfig;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.migration.ddl.DdlRunner;
import io.ebean.util.JdbcClose;
import io.ebeaninternal.api.SpiEbeanServer;
import io.ebeaninternal.dbmigration.model.CurrentModel;
import io.ebeaninternal.dbmigration.model.MTable;
import io.ebeaninternal.extraddl.model.ExtraDdlXmlReader;
import io.ebeaninternal.server.deploy.PartitionMeta;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -180,10 +183,50 @@ public class DdlGenerator {
String ignoreExtraDdl = System.getProperty("ebean.ignoreExtraDdl");
if (!"true".equalsIgnoreCase(ignoreExtraDdl) && jaxbPresent) {
if (currentModel.isTablePartitioning()) {
String extraPartitioning = ExtraDdlXmlReader.buildPartitioning(server.getDatabasePlatform().getName());
if (extraPartitioning != null && !extraPartitioning.isEmpty()) {
runScript(connection, false, extraPartitioning, "builtin-partitioning-dll");
}
}
String extraApply = ExtraDdlXmlReader.buildExtra(server.getDatabasePlatform().getName(), false);
if (extraApply != null) {
runScript(connection, false, extraApply, "extra-dll");
}
if (currentModel.isTablePartitioning()) {
checkInitialTablePartitions(connection);
}
}
}
/**
* Check if table partitions exist and if not create some. The expectation is that
* extra-dll.xml should have some partition initialisation but this helps people get going.
*/
private void checkInitialTablePartitions(Connection connection) {
DatabasePlatform databasePlatform = server.getDatabasePlatform();
try {
StringBuilder sb = new StringBuilder();
for (MTable table : currentModel.getPartitionedTables()) {
String tableName = table.getName();
if (!databasePlatform.tablePartitionsExist(connection, tableName)) {
log.info("No table partitions for table {}", tableName);
PartitionMeta meta = table.getPartitionMeta();
String initPart = databasePlatform.tablePartitionInit(tableName, meta.getMode(), meta.getProperty(), table.singlePrimaryKey());
sb.append(initPart).append("\n");
}
}
String initialPartitionSql = sb.toString();
if (!initialPartitionSql.isEmpty()) {
runScript(connection, false, initialPartitionSql, "initial table partitions");
}
} catch (SQLException e) {
log.error("Error checking initial table partitions", e);
}
}
@@ -84,6 +84,8 @@ public class DefaultDbMigration implements DbMigration {
protected DatabasePlatform databasePlatform;
private boolean vanillaPlatform;
protected List<Pair> platforms = new ArrayList<>();
protected ServerConfig serverConfig;
@@ -98,6 +100,8 @@ public class DefaultDbMigration implements DbMigration {
protected String name;
protected String generatePendingDrop;
protected boolean includeBuiltInPartitioning = true;
/**
* Create for offline migration generation.
*/
@@ -179,6 +183,11 @@ public class DefaultDbMigration implements DbMigration {
this.includeGeneratedFileComment = includeGeneratedFileComment;
}
@Override
public void setIncludeBuiltInPartitioning(boolean includeBuiltInPartitioning) {
this.includeBuiltInPartitioning = includeBuiltInPartitioning;
}
@Override
public void setHeader(String header) {
this.header = header;
@@ -192,6 +201,7 @@ public class DefaultDbMigration implements DbMigration {
*/
@Override
public void setPlatform(Platform platform) {
vanillaPlatform = true;
setPlatform(getPlatform(platform));
}
@@ -276,7 +286,7 @@ public class DefaultDbMigration implements DbMigration {
try {
Request request = createRequest();
if (platforms.isEmpty()) {
generateExtraDdl(request.migrationDir, databasePlatform);
generateExtraDdl(request.migrationDir, databasePlatform, request.isTablePartitioning());
}
String pendingVersion = generatePendingDrop();
@@ -311,24 +321,27 @@ public class DefaultDbMigration implements DbMigration {
* migration runner.
* </p>
*/
private void generateExtraDdl(File migrationDir, DatabasePlatform dbPlatform) throws IOException {
private void generateExtraDdl(File migrationDir, DatabasePlatform dbPlatform, boolean tablePartitioning) throws IOException {
if (dbPlatform != null) {
generateExtraDdl(migrationDir, dbPlatform, ExtraDdlXmlReader.readBuiltin());
generateExtraDdl(migrationDir, dbPlatform, ExtraDdlXmlReader.read());
if (tablePartitioning && includeBuiltInPartitioning) {
generateExtraDdlFor(migrationDir, dbPlatform, ExtraDdlXmlReader.readBuiltinTablePartitioning());
}
generateExtraDdlFor(migrationDir, dbPlatform, ExtraDdlXmlReader.readBuiltin());
generateExtraDdlFor(migrationDir, dbPlatform, ExtraDdlXmlReader.read());
}
}
private void generateExtraDdl(File migrationDir, DatabasePlatform dbPlatform, ExtraDdl extraDdl) throws IOException {
private void generateExtraDdlFor(File migrationDir, DatabasePlatform dbPlatform, ExtraDdl extraDdl) throws IOException {
if (extraDdl != null) {
List<DdlScript> ddlScript = extraDdl.getDdlScript();
for (DdlScript script : ddlScript) {
if (!script.isDrop() && ExtraDdlXmlReader.matchPlatform(dbPlatform.getName(), script.getPlatforms())) {
writeExtraDdl(migrationDir, script);
}
writeExtraDdl(migrationDir, script);
}
}
}
}
/**
* Write (or override) the "repeatable" migration script.
@@ -346,8 +359,6 @@ public class DefaultDbMigration implements DbMigration {
}
}
private String repeatableMigrationName(boolean init, String scriptName) {
StringBuilder sb = new StringBuilder();
if (init) {
@@ -418,6 +429,10 @@ public class DefaultDbMigration implements DbMigration {
this.current = currentModel.read();
}
boolean isTablePartitioning() {
return current.isTablePartitioning();
}
/**
* Return the migration for the pending drops for a given version.
*/
@@ -525,7 +540,7 @@ public class DefaultDbMigration implements DbMigration {
File subPath = platformWriter.subPath(writePath, pair.prefix);
platformWriter.processMigration(dbMigration, platformBuffer, subPath, fullVersion);
generateExtraDdl(subPath, pair.platform);
generateExtraDdl(subPath, pair.platform, currentModel.isTablePartitioning());
}
}
@@ -556,11 +571,10 @@ public class DefaultDbMigration implements DbMigration {
if (server == null) {
setServer(Ebean.getDefaultServer());
}
if (databasePlatform == null && platforms.isEmpty()) {
// not explicitly set not set a list of platforms so
// default to the platform of the default server
if (vanillaPlatform || databasePlatform == null) {
// not explicitly set so use the platform of the server
databasePlatform = server.getDatabasePlatform();
logger.debug("set platform to {}", databasePlatform.getName());
logger.trace("set platform to {}", databasePlatform.getName());
}
if (migrationConfig != null) {
if (strictMode != null) {
@@ -257,6 +257,8 @@ public class BaseTableDdl implements TableDdl {
useSequence = (IdType.SEQUENCE == useDbIdentityType);
}
String partitionMode = createTable.getPartitionMode();
DdlBuffer apply = writer.apply();
apply.append("create table ").append(tableName).append(" (");
writeTableColumns(apply, columns, useIdentity);
@@ -265,7 +267,9 @@ public class BaseTableDdl implements TableDdl {
writeCompoundUniqueConstraints(apply, createTable);
if (!pk.isEmpty()) {
// defined on the columns
writePrimaryKeyConstraint(apply, createTable.getPkName(), toColumnNames(pk));
if (partitionMode == null || !platformDdl.suppressPrimaryKeyOnPartition()) {
writePrimaryKeyConstraint(apply, createTable.getPkName(), toColumnNames(pk));
}
}
if (platformDdl.isInlineForeignKeys()) {
writeInlineForeignKeys(writer, createTable);
@@ -273,6 +277,9 @@ public class BaseTableDdl implements TableDdl {
apply.newLine().append(")");
addTableCommentInline(apply, createTable);
if (partitionMode != null) {
platformDdl.addTablePartition(apply, partitionMode, createTable.getPartitionColumn());
}
apply.endOfStatement();
addComments(apply, createTable);
@@ -662,4 +662,12 @@ public class PlatformDdl {
public void unlockTables(DdlBuffer buffer, Collection<String> tables) throws IOException {
}
public boolean suppressPrimaryKeyOnPartition() {
return false;
}
public void addTablePartition(DdlBuffer apply, String partitionMode, String partitionColumn) throws IOException {
// only supported by postgres initially
}
}
@@ -1,6 +1,9 @@
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer;
import java.io.IOException;
/**
* Postgres specific DDL.
@@ -16,6 +19,11 @@ public class PostgresDdl extends PlatformDdl {
this.columnSetNull = "drop not null";
}
@Override
public boolean suppressPrimaryKeyOnPartition() {
return true;
}
@Override
protected String convertArrayType(String logicalArrayType) {
return NativeDbArray.logicalToNative(logicalArrayType);
@@ -38,4 +46,9 @@ public class PostgresDdl extends PlatformDdl {
}
return columnDefn;
}
@Override
public void addTablePartition(DdlBuffer apply, String partitionMode, String partitionColumn) throws IOException {
apply.append(" partition by range (").append(partitionColumn).append(")");
}
}
@@ -12,9 +12,9 @@ import java.util.List;
/**
* <p>Java class for anonymous complex type.
* <p>
*
* <p>The following schema fragment specifies the expected content contained within this class.
* <p>
*
* <pre>
* &lt;complexType>
* &lt;complexContent>
@@ -45,20 +45,20 @@ public class AddColumn {
/**
* Gets the value of the column property.
* <p>
*
* <p>
* This accessor method returns a reference to the live list,
* not a snapshot. Therefore any modification you make to the
* returned list will be present inside the JAXB object.
* This is why there is not a <CODE>set</CODE> method for the column property.
* <p>
*
* <p>
* For example, to add a new item, do as follows:
* <pre>
* getColumn().add(newItem);
* </pre>
* <p>
* <p>
*
*
* <p>
* Objects of the following type(s) are allowed in the list
* {@link Column }
@@ -9,9 +9,9 @@ import javax.xml.bind.annotation.XmlType;
/**
* <p>Java class for anonymous complex type.
* <p>
*
* <p>The following schema fragment specifies the expected content contained within this class.
* <p>
*
* <pre>
* &lt;complexType>
* &lt;complexContent>
@@ -9,9 +9,9 @@ import javax.xml.bind.annotation.XmlType;
/**
* <p>Java class for anonymous complex type.
* <p>
*
* <p>The following schema fragment specifies the expected content contained within this class.
* <p>
*
* <pre>
* &lt;complexType>
* &lt;complexContent>
@@ -9,11 +9,21 @@ import javax.xml.bind.annotation.XmlType;
/**
* <p>Java class for anonymous complex type.
* <p>
*
* <p>The following schema fragment specifies the expected content contained within this class.
* <p>
*
* <pre>
* TODO
* &lt;complexType>
* &lt;complexContent>
* &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
* &lt;attribute name="constraintName" 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="columnNames" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="oneToOne" type="{http://www.w3.org/2001/XMLSchema}boolean" />
* &lt;attribute name="nullableColumns" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;/restriction>
* &lt;/complexContent>
* &lt;/complexType>
* </pre>
*/
@XmlAccessorType(XmlAccessType.FIELD)
@@ -23,18 +33,14 @@ public class AddUniqueConstraint {
@XmlAttribute(name = "constraintName", required = true)
protected String constraintName;
@XmlAttribute(name = "tableName", required = true)
protected String tableName;
@XmlAttribute(name = "columnNames", required = true)
protected String columnNames;
@XmlAttribute(name = "nullableColumns", required = true)
protected String nullableColumns;
@XmlAttribute(name = "oneToOne", required = false)
@XmlAttribute(name = "oneToOne")
protected Boolean oneToOne;
@XmlAttribute(name = "nullableColumns")
protected String nullableColumns;
/**
* Gets the value of the constraintName property.
@@ -55,7 +61,7 @@ public class AddUniqueConstraint {
public void setConstraintName(String value) {
this.constraintName = value;
}
/**
* Gets the value of the tableName property.
*
@@ -65,15 +71,15 @@ public class AddUniqueConstraint {
public String getTableName() {
return tableName;
}
/**
* Sets the value of the tableName property.
*
* @param value allowed object is
* {@link String }
*/
public void setTableName(String tableName) {
this.tableName = tableName;
public void setTableName(String value) {
this.tableName = value;
}
/**
@@ -96,6 +102,26 @@ public class AddUniqueConstraint {
this.columnNames = value;
}
/**
* Gets the value of the oneToOne property.
*
* @return possible object is
* {@link Boolean }
*/
public Boolean isOneToOne() {
return oneToOne;
}
/**
* Sets the value of the oneToOne property.
*
* @param value allowed object is
* {@link Boolean }
*/
public void setOneToOne(Boolean value) {
this.oneToOne = value;
}
/**
* Gets the value of the nullableColumns property.
*
@@ -115,23 +141,5 @@ public class AddUniqueConstraint {
public void setNullableColumns(String value) {
this.nullableColumns = value;
}
/**
* Gets the value of the oneToOne property.
*
* @return true if oneToOne was set
*/
public boolean isOneToOne() {
return Boolean.TRUE.equals(oneToOne);
}
/**
* Sets the value of the oneToOne property.
*
* @param value boolean
*/
public void setOneToOne(boolean oneToOne) {
this.oneToOne = oneToOne;
}
}
@@ -3,7 +3,6 @@ package io.ebeaninternal.dbmigration.migration;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import java.util.ArrayList;
@@ -12,13 +11,17 @@ import java.util.List;
/**
* <p>Java class for anonymous complex type.
* <p>
*
* <p>The following schema fragment specifies the expected content contained within this class.
* <p>
*
* <pre>
* &lt;complexType>
* &lt;complexContent>
* &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
* &lt;sequence>
* &lt;element name="before" type="{http://ebean-orm.github.io/xml/ns/dbmigration}ddl-script" maxOccurs="unbounded" minOccurs="0"/>
* &lt;element name="after" type="{http://ebean-orm.github.io/xml/ns/dbmigration}ddl-script" maxOccurs="unbounded" minOccurs="0"/>
* &lt;/sequence>
* &lt;attribute name="columnName" 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="withHistory" type="{http://www.w3.org/2001/XMLSchema}boolean" />
@@ -50,16 +53,14 @@ import java.util.List;
*/
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
"before", "after"
"before",
"after"
})
@XmlRootElement(name = "alterColumn")
public class AlterColumn {
@XmlElement(required = false)
protected List<DdlScript> before;
@XmlElement(required = false)
protected List<DdlScript> after;
@XmlAttribute(name = "columnName", required = true)
protected String columnName;
@XmlAttribute(name = "tableName", required = true)
@@ -109,6 +110,60 @@ public class AlterColumn {
@XmlAttribute(name = "dropForeignKeyIndex")
protected String dropForeignKeyIndex;
/**
* Gets the value of the before property.
*
* <p>
* This accessor method returns a reference to the live list,
* not a snapshot. Therefore any modification you make to the
* returned list will be present inside the JAXB object.
* This is why there is not a <CODE>set</CODE> method for the before property.
*
* <p>
* For example, to add a new item, do as follows:
* <pre>
* getBefore().add(newItem);
* </pre>
*
*
* <p>
* Objects of the following type(s) are allowed in the list
* {@link DdlScript }
*/
public List<DdlScript> getBefore() {
if (before == null) {
before = new ArrayList<>();
}
return this.before;
}
/**
* Gets the value of the after property.
*
* <p>
* This accessor method returns a reference to the live list,
* not a snapshot. Therefore any modification you make to the
* returned list will be present inside the JAXB object.
* This is why there is not a <CODE>set</CODE> method for the after property.
*
* <p>
* For example, to add a new item, do as follows:
* <pre>
* getAfter().add(newItem);
* </pre>
*
*
* <p>
* Objects of the following type(s) are allowed in the list
* {@link DdlScript }
*/
public List<DdlScript> getAfter() {
if (after == null) {
after = new ArrayList<>();
}
return this.after;
}
/**
* Gets the value of the columnName property.
*
@@ -589,17 +644,4 @@ public class AlterColumn {
this.dropForeignKeyIndex = value;
}
public List<DdlScript> getBefore() {
if (before == null) {
before = new ArrayList<>();
}
return before;
}
public List<DdlScript> getAfter() {
if (after == null) {
after = new ArrayList<>();
}
return after;
}
}
@@ -9,21 +9,34 @@ import javax.xml.bind.annotation.XmlType;
/**
* <p>Java class for anonymous complex type.
* <p>
*
* <p>The following schema fragment specifies the expected content contained within this class.
* <p>
*
* <pre>
* TODO
* &lt;complexType>
* &lt;complexContent>
* &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
* &lt;attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="columnNames" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="refColumnNames" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="refTableName" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="indexName" 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="onDelete" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="onUpdate" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;/restriction>
* &lt;/complexContent>
* &lt;/complexType>
* </pre>
*/
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "")
@XmlRootElement(name = "foreignKey")
@XmlRootElement(name = "alterForeignKey")
public class AlterForeignKey {
@XmlAttribute(name = "name", required = true)
protected String name;
@XmlAttribute(name = "columnNames", required = true)
@XmlAttribute(name = "columnNames")
protected String columnNames;
@XmlAttribute(name = "refColumnNames")
protected String refColumnNames;
@@ -37,6 +50,7 @@ public class AlterForeignKey {
protected String onDelete;
@XmlAttribute(name = "onUpdate")
protected String onUpdate;
/**
* Gets the value of the name property.
*
@@ -196,4 +210,5 @@ public class AlterForeignKey {
public void setOnUpdate(String value) {
this.onUpdate = value;
}
}
@@ -9,9 +9,9 @@ import javax.xml.bind.annotation.XmlType;
/**
* <p>Java class for anonymous complex type.
* <p>
*
* <p>The following schema fragment specifies the expected content contained within this class.
* <p>
*
* <pre>
* &lt;complexType>
* &lt;complexContent>
@@ -9,9 +9,9 @@ import javax.xml.bind.annotation.XmlValue;
/**
* <p>Java class for anonymous complex type.
* <p>
*
* <p>The following schema fragment specifies the expected content contained within this class.
* <p>
*
* <pre>
* &lt;complexType>
* &lt;simpleContent>
@@ -13,9 +13,9 @@ import java.util.List;
/**
* <p>Java class for anonymous complex type.
* <p>
*
* <p>The following schema fragment specifies the expected content contained within this class.
* <p>
*
* <pre>
* &lt;complexType>
* &lt;complexContent>
@@ -50,16 +50,16 @@ public class ChangeSet {
@XmlElement(name = "dropTable", type = DropTable.class),
@XmlElement(name = "renameTable", type = RenameTable.class),
@XmlElement(name = "addTableComment", type = AddTableComment.class),
@XmlElement(name = "addUniqueConstraint", type = AddUniqueConstraint.class),
@XmlElement(name = "addHistoryTable", type = AddHistoryTable.class),
@XmlElement(name = "dropHistoryTable", type = DropHistoryTable.class),
@XmlElement(name = "alterForeignKey", type = AlterForeignKey.class),
@XmlElement(name = "addColumn", type = AddColumn.class),
@XmlElement(name = "dropColumn", type = DropColumn.class),
@XmlElement(name = "alterColumn", type = AlterColumn.class),
@XmlElement(name = "renameColumn", type = RenameColumn.class),
@XmlElement(name = "createIndex", type = CreateIndex.class),
@XmlElement(name = "dropIndex", type = DropIndex.class),
@XmlElement(name = "addUniqueConstraint", type = AddUniqueConstraint.class),
@XmlElement(name = "alterForeignKey", type = AlterForeignKey.class),
@XmlElement(name = "dropIndex", type = DropIndex.class)
})
protected List<Object> changeSetChildren;
@XmlAttribute(name = "type", required = true)
@@ -77,20 +77,20 @@ public class ChangeSet {
/**
* Gets the value of the changeSetChildren property.
* <p>
*
* <p>
* This accessor method returns a reference to the live list,
* not a snapshot. Therefore any modification you make to the
* returned list will be present inside the JAXB object.
* This is why there is not a <CODE>set</CODE> method for the changeSetChildren property.
* <p>
*
* <p>
* For example, to add a new item, do as follows:
* <pre>
* getChangeSetChildren().add(newItem);
* </pre>
* <p>
* <p>
*
*
* <p>
* Objects of the following type(s) are allowed in the list
* {@link Configuration }
@@ -99,8 +99,10 @@ public class ChangeSet {
* {@link DropTable }
* {@link RenameTable }
* {@link AddTableComment }
* {@link AddUniqueConstraint }
* {@link AddHistoryTable }
* {@link DropHistoryTable }
* {@link AlterForeignKey }
* {@link AddColumn }
* {@link DropColumn }
* {@link AlterColumn }
@@ -7,7 +7,7 @@ import javax.xml.bind.annotation.XmlType;
/**
* <p>Java class for changeSetType.
* <p>
*
* <p>The following schema fragment specifies the expected content contained within this class.
* <p>
* <pre>
@@ -16,6 +16,7 @@ import javax.xml.bind.annotation.XmlType;
* &lt;enumeration value="apply"/>
* &lt;enumeration value="pendingDrops"/>
* &lt;enumeration value="baseline"/>
* &lt;enumeration value="drop"/>
* &lt;/restriction>
* &lt;/simpleType>
* </pre>
@@ -29,7 +30,9 @@ public enum ChangeSetType {
@XmlEnumValue("pendingDrops")
PENDING_DROPS("pendingDrops"),
@XmlEnumValue("baseline")
BASELINE("baseline");
BASELINE("baseline"),
@XmlEnumValue("drop")
DROP("drop");
private final String value;
ChangeSetType(String v) {
@@ -3,7 +3,6 @@ package io.ebeaninternal.dbmigration.migration;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import java.util.ArrayList;
@@ -12,13 +11,17 @@ import java.util.List;
/**
* <p>Java class for anonymous complex type.
* <p>
*
* <p>The following schema fragment specifies the expected content contained within this class.
* <p>
*
* <pre>
* &lt;complexType>
* &lt;complexContent>
* &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
* &lt;sequence>
* &lt;element name="before" type="{http://ebean-orm.github.io/xml/ns/dbmigration}ddl-script" maxOccurs="unbounded" minOccurs="0"/>
* &lt;element name="after" type="{http://ebean-orm.github.io/xml/ns/dbmigration}ddl-script" maxOccurs="unbounded" minOccurs="0"/>
* &lt;/sequence>
* &lt;attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="type" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="defaultValue" type="{http://www.w3.org/2001/XMLSchema}string" />
@@ -43,16 +46,14 @@ import java.util.List;
*/
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
"before", "after"
"before",
"after"
})
@XmlRootElement(name = "column")
public class Column {
@XmlElement(required = false)
protected List<DdlScript> before;
@XmlElement(required = false)
protected List<DdlScript> after;
@XmlAttribute(name = "name", required = true)
protected String name;
@XmlAttribute(name = "type", required = true)
@@ -88,19 +89,58 @@ public class Column {
@XmlAttribute(name = "comment")
protected String comment;
/**
* Gets the value of the before property.
*
* <p>
* This accessor method returns a reference to the live list,
* not a snapshot. Therefore any modification you make to the
* returned list will be present inside the JAXB object.
* This is why there is not a <CODE>set</CODE> method for the before property.
*
* <p>
* For example, to add a new item, do as follows:
* <pre>
* getBefore().add(newItem);
* </pre>
*
*
* <p>
* Objects of the following type(s) are allowed in the list
* {@link DdlScript }
*/
public List<DdlScript> getBefore() {
if (before == null) {
before = new ArrayList<>();
}
return before;
return this.before;
}
/**
* Gets the value of the after property.
*
* <p>
* This accessor method returns a reference to the live list,
* not a snapshot. Therefore any modification you make to the
* returned list will be present inside the JAXB object.
* This is why there is not a <CODE>set</CODE> method for the after property.
*
* <p>
* For example, to add a new item, do as follows:
* <pre>
* getAfter().add(newItem);
* </pre>
*
*
* <p>
* Objects of the following type(s) are allowed in the list
* {@link DdlScript }
*/
public List<DdlScript> getAfter() {
if (after == null) {
after = new ArrayList<>();
}
return after;
return this.after;
}
/**
@@ -384,7 +424,7 @@ public class Column {
}
/**
* Gets the value of the foreignOnDelete property.
* Gets the value of the foreignKeyOnDelete property.
*
* @return possible object is
* {@link String }
@@ -404,7 +444,7 @@ public class Column {
}
/**
* Gets the value of the foreignOnUpdate property.
* Gets the value of the foreignKeyOnUpdate property.
*
* @return possible object is
* {@link String }
@@ -9,9 +9,9 @@ import javax.xml.bind.annotation.XmlType;
/**
* <p>Java class for anonymous complex type.
* <p>
*
* <p>The following schema fragment specifies the expected content contained within this class.
* <p>
*
* <pre>
* &lt;complexType>
* &lt;complexContent>
@@ -9,9 +9,9 @@ import javax.xml.bind.annotation.XmlType;
/**
* <p>Java class for anonymous complex type.
* <p>
*
* <p>The following schema fragment specifies the expected content contained within this class.
* <p>
*
* <pre>
* &lt;complexType>
* &lt;complexContent>
@@ -14,9 +14,9 @@ import java.util.List;
/**
* <p>Java class for anonymous complex type.
* <p>
*
* <p>The following schema fragment specifies the expected content contained within this class.
* <p>
*
* <pre>
* &lt;complexType>
* &lt;complexContent>
@@ -28,6 +28,8 @@ import java.util.List;
* &lt;/sequence>
* &lt;attGroup ref="{http://ebean-orm.github.io/xml/ns/dbmigration}tablespaceAttributes"/>
* &lt;attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="partitionMode" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="partitionColumn" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="withHistory" type="{http://www.w3.org/2001/XMLSchema}boolean" />
* &lt;attribute name="draft" type="{http://www.w3.org/2001/XMLSchema}boolean" />
* &lt;attribute name="identityType" type="{http://ebean-orm.github.io/xml/ns/dbmigration}identityType" />
@@ -55,6 +57,10 @@ public class CreateTable {
protected List<ForeignKey> foreignKey;
@XmlAttribute(name = "name", required = true)
protected String name;
@XmlAttribute(name = "partitionMode")
protected String partitionMode;
@XmlAttribute(name = "partitionColumn")
protected String partitionColumn;
@XmlAttribute(name = "withHistory")
protected Boolean withHistory;
@XmlAttribute(name = "draft")
@@ -80,20 +86,20 @@ public class CreateTable {
/**
* Gets the value of the column property.
* <p>
*
* <p>
* This accessor method returns a reference to the live list,
* not a snapshot. Therefore any modification you make to the
* returned list will be present inside the JAXB object.
* This is why there is not a <CODE>set</CODE> method for the column property.
* <p>
*
* <p>
* For example, to add a new item, do as follows:
* <pre>
* getColumn().add(newItem);
* </pre>
* <p>
* <p>
*
*
* <p>
* Objects of the following type(s) are allowed in the list
* {@link Column }
@@ -107,20 +113,20 @@ public class CreateTable {
/**
* Gets the value of the uniqueConstraint property.
* <p>
*
* <p>
* This accessor method returns a reference to the live list,
* not a snapshot. Therefore any modification you make to the
* returned list will be present inside the JAXB object.
* This is why there is not a <CODE>set</CODE> method for the uniqueConstraint property.
* <p>
*
* <p>
* For example, to add a new item, do as follows:
* <pre>
* getUniqueConstraint().add(newItem);
* </pre>
* <p>
* <p>
*
*
* <p>
* Objects of the following type(s) are allowed in the list
* {@link UniqueConstraint }
@@ -134,20 +140,20 @@ public class CreateTable {
/**
* Gets the value of the foreignKey property.
* <p>
*
* <p>
* This accessor method returns a reference to the live list,
* not a snapshot. Therefore any modification you make to the
* returned list will be present inside the JAXB object.
* This is why there is not a <CODE>set</CODE> method for the foreignKey property.
* <p>
*
* <p>
* For example, to add a new item, do as follows:
* <pre>
* getForeignKey().add(newItem);
* </pre>
* <p>
* <p>
*
*
* <p>
* Objects of the following type(s) are allowed in the list
* {@link ForeignKey }
@@ -179,6 +185,46 @@ public class CreateTable {
this.name = value;
}
/**
* Gets the value of the partitionMode property.
*
* @return possible object is
* {@link String }
*/
public String getPartitionMode() {
return partitionMode;
}
/**
* Sets the value of the partitionMode property.
*
* @param value allowed object is
* {@link String }
*/
public void setPartitionMode(String value) {
this.partitionMode = value;
}
/**
* Gets the value of the partitionColumn property.
*
* @return possible object is
* {@link String }
*/
public String getPartitionColumn() {
return partitionColumn;
}
/**
* Sets the value of the partitionColumn property.
*
* @param value allowed object is
* {@link String }
*/
public void setPartitionColumn(String value) {
this.partitionColumn = value;
}
/**
* Gets the value of the withHistory property.
*
@@ -1,42 +1,61 @@
package io.ebeaninternal.dbmigration.migration;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.XmlValue;
import java.util.ArrayList;
import java.util.List;
/**
* <p>Java class for anonymous complex type.
* <p>
* <p>Java class for ddl-script complex type.
*
* <p>The following schema fragment specifies the expected content contained within this class.
* <p>
*
* <pre>
* TODO @Rob: Can this generated automatically?
* &lt;complexType name="ddl-script">
* &lt;complexContent>
* &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
* &lt;sequence>
* &lt;element name="ddl" type="{http://www.w3.org/2001/XMLSchema}string" maxOccurs="unbounded"/>
* &lt;/sequence>
* &lt;attribute name="platforms" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;/restriction>
* &lt;/complexContent>
* &lt;/complexType>
* </pre>
*/
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
@XmlType(name = "ddl-script", propOrder = {
"ddl"
})
@XmlRootElement(name = "ddl-script")
public class DdlScript {
@XmlValue
@XmlElement(required = true)
protected List<String> ddl;
@XmlAttribute(name = "platforms")
protected String platforms;
/**
* Gets the value of the value property.
* Gets the value of the ddl property.
*
* @return possible object is
* <p>
* This accessor method returns a reference to the live list,
* not a snapshot. Therefore any modification you make to the
* returned list will be present inside the JAXB object.
* This is why there is not a <CODE>set</CODE> method for the ddl property.
*
* <p>
* For example, to add a new item, do as follows:
* <pre>
* getDdl().add(newItem);
* </pre>
*
*
* <p>
* Objects of the following type(s) are allowed in the list
* {@link String }
*/
public List<String> getDdl() {
@@ -9,9 +9,9 @@ import javax.xml.bind.annotation.XmlType;
/**
* <p>Java class for anonymous complex type.
* <p>
*
* <p>The following schema fragment specifies the expected content contained within this class.
* <p>
*
* <pre>
* &lt;complexType>
* &lt;complexContent>
@@ -9,9 +9,9 @@ import javax.xml.bind.annotation.XmlType;
/**
* <p>Java class for anonymous complex type.
* <p>
*
* <p>The following schema fragment specifies the expected content contained within this class.
* <p>
*
* <pre>
* &lt;complexType>
* &lt;complexContent>
@@ -9,9 +9,9 @@ import javax.xml.bind.annotation.XmlType;
/**
* <p>Java class for anonymous complex type.
* <p>
*
* <p>The following schema fragment specifies the expected content contained within this class.
* <p>
*
* <pre>
* &lt;complexType>
* &lt;complexContent>
@@ -9,9 +9,9 @@ import javax.xml.bind.annotation.XmlType;
/**
* <p>Java class for anonymous complex type.
* <p>
*
* <p>The following schema fragment specifies the expected content contained within this class.
* <p>
*
* <pre>
* &lt;complexType>
* &lt;complexContent>
@@ -9,14 +9,16 @@ import javax.xml.bind.annotation.XmlType;
/**
* <p>Java class for anonymous complex type.
* <p>
*
* <p>The following schema fragment specifies the expected content contained within this class.
* <p>
*
* <pre>
* &lt;complexType>
* &lt;complexContent>
* &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
* &lt;attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="sequenceCol" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="sequenceName" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;/restriction>
* &lt;/complexContent>
* &lt;/complexType>
@@ -64,16 +66,6 @@ public class DropTable {
return sequenceCol;
}
/**
* Gets the value of the sequenceName property.
*
* @return possible object is
* {@link String }
*/
public String getSequenceName() {
return sequenceName;
}
/**
* Sets the value of the sequenceCol property.
*
@@ -84,6 +76,16 @@ public class DropTable {
this.sequenceCol = value;
}
/**
* Gets the value of the sequenceName property.
*
* @return possible object is
* {@link String }
*/
public String getSequenceName() {
return sequenceName;
}
/**
* Sets the value of the sequenceName property.
*
@@ -9,9 +9,9 @@ import javax.xml.bind.annotation.XmlType;
/**
* <p>Java class for anonymous complex type.
* <p>
*
* <p>The following schema fragment specifies the expected content contained within this class.
* <p>
*
* <pre>
* &lt;complexType>
* &lt;complexContent>
@@ -47,6 +47,7 @@ public class ForeignKey {
protected String onDelete;
@XmlAttribute(name = "onUpdate")
protected String onUpdate;
/**
* Gets the value of the name property.
*
@@ -7,7 +7,7 @@ import javax.xml.bind.annotation.XmlType;
/**
* <p>Java class for identityType.
* <p>
*
* <p>The following schema fragment specifies the expected content contained within this class.
* <p>
* <pre>
@@ -11,9 +11,9 @@ import java.util.List;
/**
* <p>Java class for anonymous complex type.
* <p>
*
* <p>The following schema fragment specifies the expected content contained within this class.
* <p>
*
* <pre>
* &lt;complexType>
* &lt;complexContent>
@@ -38,20 +38,20 @@ public class Migration {
/**
* Gets the value of the changeSet property.
* <p>
*
* <p>
* This accessor method returns a reference to the live list,
* not a snapshot. Therefore any modification you make to the
* returned list will be present inside the JAXB object.
* This is why there is not a <CODE>set</CODE> method for the changeSet property.
* <p>
*
* <p>
* For example, to add a new item, do as follows:
* <pre>
* getChangeSet().add(newItem);
* </pre>
* <p>
* <p>
*
*
* <p>
* Objects of the following type(s) are allowed in the list
* {@link ChangeSet }
@@ -6,7 +6,7 @@ import javax.xml.bind.annotation.XmlRegistry;
/**
* This object contains factory methods for each
* Java content interface and Java element interface
* generated in the io.ebean.dbmigration.migration package.
* generated in the io.ebeaninternal.dbmigration.migration package.
* <p>An ObjectFactory allows you to programatically
* construct new instances of the Java representation
* for XML content. The Java representation of XML
@@ -21,23 +21,23 @@ public class ObjectFactory {
/**
* Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: io.ebean.dbmigration.migration
* Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: io.ebeaninternal.dbmigration.migration
*/
public ObjectFactory() {
}
/**
* Create an instance of {@link Rollback }
* Create an instance of {@link AddUniqueConstraint }
*/
public Rollback createRollback() {
return new Rollback();
public AddUniqueConstraint createAddUniqueConstraint() {
return new AddUniqueConstraint();
}
/**
* Create an instance of {@link AddColumn }
* Create an instance of {@link CreateTable }
*/
public AddColumn createAddColumn() {
return new AddColumn();
public CreateTable createCreateTable() {
return new CreateTable();
}
/**
@@ -48,10 +48,10 @@ public class ObjectFactory {
}
/**
* Create an instance of {@link CreateTable }
* Create an instance of {@link DdlScript }
*/
public CreateTable createCreateTable() {
return new CreateTable();
public DdlScript createDdlScript() {
return new DdlScript();
}
/**
@@ -68,13 +68,6 @@ public class ObjectFactory {
return new ForeignKey();
}
/**
* Create an instance of {@link Apply }
*/
public Apply createApply() {
return new Apply();
}
/**
* Create an instance of {@link Configuration }
*/
@@ -89,13 +82,6 @@ public class ObjectFactory {
return new DefaultTablespace();
}
/**
* Create an instance of {@link AddTableComment }
*/
public AddTableComment createAddTableComment() {
return new AddTableComment();
}
/**
* Create an instance of {@link RenameTable }
*/
@@ -111,10 +97,10 @@ public class ObjectFactory {
}
/**
* Create an instance of {@link AlterColumn }
* Create an instance of {@link AlterForeignKey }
*/
public AlterColumn createAlterColumn() {
return new AlterColumn();
public AlterForeignKey createAlterForeignKey() {
return new AlterForeignKey();
}
/**
@@ -124,6 +110,69 @@ public class ObjectFactory {
return new DropColumn();
}
/**
* Create an instance of {@link Sql }
*/
public Sql createSql() {
return new Sql();
}
/**
* Create an instance of {@link Apply }
*/
public Apply createApply() {
return new Apply();
}
/**
* Create an instance of {@link Rollback }
*/
public Rollback createRollback() {
return new Rollback();
}
/**
* Create an instance of {@link DropIndex }
*/
public DropIndex createDropIndex() {
return new DropIndex();
}
/**
* Create an instance of {@link RenameColumn }
*/
public RenameColumn createRenameColumn() {
return new RenameColumn();
}
/**
* Create an instance of {@link DropTable }
*/
public DropTable createDropTable() {
return new DropTable();
}
/**
* Create an instance of {@link AddColumn }
*/
public AddColumn createAddColumn() {
return new AddColumn();
}
/**
* Create an instance of {@link AddTableComment }
*/
public AddTableComment createAddTableComment() {
return new AddTableComment();
}
/**
* Create an instance of {@link AlterColumn }
*/
public AlterColumn createAlterColumn() {
return new AlterColumn();
}
/**
* Create an instance of {@link CreateIndex }
*/
@@ -138,20 +187,6 @@ public class ObjectFactory {
return new ChangeSet();
}
/**
* Create an instance of {@link Sql }
*/
public Sql createSql() {
return new Sql();
}
/**
* Create an instance of {@link DropTable }
*/
public DropTable createDropTable() {
return new DropTable();
}
/**
* Create an instance of {@link AddHistoryTable }
*/
@@ -159,20 +194,6 @@ public class ObjectFactory {
return new AddHistoryTable();
}
/**
* Create an instance of {@link RenameColumn }
*/
public RenameColumn createRenameColumn() {
return new RenameColumn();
}
/**
* Create an instance of {@link DropIndex }
*/
public DropIndex createDropIndex() {
return new DropIndex();
}
/**
* Create an instance of {@link AlterHistoryTable }
*/
@@ -187,24 +208,4 @@ public class ObjectFactory {
return new Migration();
}
/**
* Create an instance of {@link DdlScript }
*/
public DdlScript createDdlScript() {
return new DdlScript();
}
/**
* Create an instance of {@link AddUniqueConstraint }
*/
public AddUniqueConstraint createAddUniqueConstraint() {
return new AddUniqueConstraint();
}
/**
* Create an instance of {@link AddUniqueConstraint }
*/
public AlterForeignKey createAlterForeignKey() {
return new AlterForeignKey();
}
}
@@ -9,9 +9,9 @@ import javax.xml.bind.annotation.XmlType;
/**
* <p>Java class for anonymous complex type.
* <p>
*
* <p>The following schema fragment specifies the expected content contained within this class.
* <p>
*
* <pre>
* &lt;complexType>
* &lt;complexContent>
@@ -9,9 +9,9 @@ import javax.xml.bind.annotation.XmlType;
/**
* <p>Java class for anonymous complex type.
* <p>
*
* <p>The following schema fragment specifies the expected content contained within this class.
* <p>
*
* <pre>
* &lt;complexType>
* &lt;complexContent>
@@ -9,9 +9,9 @@ import javax.xml.bind.annotation.XmlValue;
/**
* <p>Java class for anonymous complex type.
* <p>
*
* <p>The following schema fragment specifies the expected content contained within this class.
* <p>
*
* <pre>
* &lt;complexType>
* &lt;simpleContent>
@@ -9,9 +9,9 @@ import javax.xml.bind.annotation.XmlType;
/**
* <p>Java class for anonymous complex type.
* <p>
*
* <p>The following schema fragment specifies the expected content contained within this class.
* <p>
*
* <pre>
* &lt;complexType>
* &lt;complexContent>
@@ -9,15 +9,17 @@ import javax.xml.bind.annotation.XmlType;
/**
* <p>Java class for anonymous complex type.
* <p>
*
* <p>The following schema fragment specifies the expected content contained within this class.
* <p>
*
* <pre>
* &lt;complexType>
* &lt;complexContent>
* &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
* &lt;attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="columnNames" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="oneToOne" type="{http://www.w3.org/2001/XMLSchema}boolean" />
* &lt;attribute name="nullableColumns" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;/restriction>
* &lt;/complexContent>
* &lt;/complexType>
@@ -30,16 +32,13 @@ public class UniqueConstraint {
@XmlAttribute(name = "name", required = true)
protected String name;
@XmlAttribute(name = "columnNames", required = true)
protected String columnNames;
@XmlAttribute(name = "oneToOne", required = false)
@XmlAttribute(name = "oneToOne")
protected Boolean oneToOne;
@XmlAttribute(name = "nullableColumns", required = true)
@XmlAttribute(name = "nullableColumns")
protected String nullableColumns;
/**
* Gets the value of the name property.
*
@@ -79,23 +78,25 @@ public class UniqueConstraint {
public void setColumnNames(String value) {
this.columnNames = value;
}
/**
* Gets the value of the oneToOne property.
*
* @return true if oneToOne was set
* @return possible object is
* {@link Boolean }
*/
public boolean isOneToOne() {
return Boolean.TRUE.equals(oneToOne);
public Boolean isOneToOne() {
return oneToOne;
}
/**
* Sets the value of the oneToOne property.
*
* @param value boolean
* @param value allowed object is
* {@link Boolean }
*/
public void setOneToOne(boolean oneToOne) {
this.oneToOne = oneToOne;
public void setOneToOne(Boolean value) {
this.oneToOne = value;
}
/**
@@ -60,6 +60,20 @@ public class CurrentModel {
this.platformTypes = platformTypes;
}
/**
* Return true if the model contains tables that are partitioned.
*/
public boolean isTablePartitioning() {
return model.isTablePartitioning();
}
/**
* Return the tables that have partitioning.
*/
public List<MTable> getPartitionedTables() {
return model.getPartitionedTables();
}
private static DbConstraintNaming.MaxLength maxLength(SpiEbeanServer server, DbConstraintNaming naming) {
if (naming.getMaxLength() != null) {
@@ -116,17 +130,7 @@ public class CurrentModel {
ddl.append(header).append('\n');
}
ExtraDdl extraDdl = ExtraDdlXmlReader.readBuiltin();
if (extraDdl != null) {
List<DdlScript> ddlScript = extraDdl.getDdlScript();
for (DdlScript script : ddlScript) {
if (script.isInit() && ExtraDdlXmlReader.matchPlatform(server.getDatabasePlatform().getName(), script.getPlatforms())) {
ddl.append("-- init script " + script.getName()).append('\n');
ddl.append(script.getValue());
}
}
}
addExtraDdl(ddl, ExtraDdlXmlReader.readBuiltin(), "-- init script ");
ddl.append(write.apply().getBuffer());
ddl.append(write.applyForeignKeys().getBuffer());
@@ -136,6 +140,18 @@ public class CurrentModel {
return ddl.toString();
}
private void addExtraDdl(StringBuilder ddl, ExtraDdl extraDdl, String prefix) {
if (extraDdl != null) {
List<DdlScript> ddlScript = extraDdl.getDdlScript();
for (DdlScript script : ddlScript) {
if (script.isInit() && ExtraDdlXmlReader.matchPlatform(server.getDatabasePlatform().getName(), script.getPlatforms())) {
ddl.append(prefix + script.getName()).append('\n');
ddl.append(script.getValue());
}
}
}
}
/**
* Return the 'Drop' DDL.
*/
@@ -1,11 +1,12 @@
package io.ebeaninternal.dbmigration.model;
import java.util.Arrays;
import java.util.Objects;
import io.ebeaninternal.dbmigration.ddlgeneration.platform.DdlHelp;
import io.ebeaninternal.dbmigration.migration.AddUniqueConstraint;
import io.ebeaninternal.dbmigration.migration.UniqueConstraint;
import java.util.Arrays;
import java.util.Objects;
/**
* A unique constraint for multiple columns.
* <p>
@@ -26,13 +27,13 @@ public class MCompoundUniqueConstraint {
* The columns combined to be unique.
*/
private final String[] columns;
private String[] nullableColumns;
public MCompoundUniqueConstraint(String[] columns, boolean oneToOne, String name) {
public MCompoundUniqueConstraint(String[] columns, Boolean oneToOne, String name) {
this.name = name;
this.columns = columns;
this.oneToOne = oneToOne;
this.oneToOne = Boolean.TRUE.equals(oneToOne);
}
/**
@@ -55,6 +56,7 @@ public class MCompoundUniqueConstraint {
public String getName() {
return name;
}
public UniqueConstraint getUniqueConstraint() {
UniqueConstraint uq = new UniqueConstraint();
uq.setName(getName());
@@ -63,6 +65,7 @@ public class MCompoundUniqueConstraint {
uq.setOneToOne(isOneToOne());
return uq;
}
/**
* Return a AddUniqueConstraint migration for this constraint.
*/
@@ -87,7 +90,7 @@ public class MCompoundUniqueConstraint {
dropUniqueConstraint.setNullableColumns(join(nullableColumns));
return dropUniqueConstraint;
}
public void setNullableColumns(String[] nullableColumns) {
if (nullableColumns != null && nullableColumns.length == 0) {
this.nullableColumns = null;
@@ -109,12 +112,12 @@ public class MCompoundUniqueConstraint {
}
return sb.toString();
}
@Override
public int hashCode() {
return Arrays.hashCode(columns) + 31 * Objects.hash(name, oneToOne);
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
@@ -125,8 +128,8 @@ public class MCompoundUniqueConstraint {
}
MCompoundUniqueConstraint other = (MCompoundUniqueConstraint) obj;
return Arrays.equals(columns, other.columns)
&& Arrays.equals(nullableColumns, other.nullableColumns)
&& Objects.equals(name, other.name)
&& oneToOne == other.oneToOne;
&& Arrays.equals(nullableColumns, other.nullableColumns)
&& Objects.equals(name, other.name)
&& oneToOne == other.oneToOne;
}
}
@@ -1,5 +1,6 @@
package io.ebeaninternal.dbmigration.model;
import io.ebean.annotation.PartitionMode;
import io.ebeaninternal.dbmigration.ddlgeneration.platform.DdlHelp;
import io.ebeaninternal.dbmigration.ddlgeneration.platform.SplitColumns;
import io.ebeaninternal.dbmigration.migration.AddColumn;
@@ -14,6 +15,7 @@ import io.ebeaninternal.dbmigration.migration.DropTable;
import io.ebeaninternal.dbmigration.migration.ForeignKey;
import io.ebeaninternal.dbmigration.migration.IdentityType;
import io.ebeaninternal.dbmigration.migration.UniqueConstraint;
import io.ebeaninternal.server.deploy.PartitionMeta;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -61,6 +63,8 @@ public class MTable {
*/
private boolean draft;
private PartitionMeta partitionMeta;
/**
* Primary key name.
*/
@@ -237,6 +241,10 @@ public class MTable {
createTable.setName(name);
createTable.setPkName(pkName);
createTable.setComment(comment);
if (partitionMeta != null) {
createTable.setPartitionMode(partitionMeta.getMode().name());
createTable.setPartitionColumn(partitionMeta.getProperty());
}
createTable.setTablespace(tablespace);
createTable.setIndexTablespace(indexTablespace);
createTable.setSequenceName(sequenceName);
@@ -419,6 +427,20 @@ public class MTable {
return draft;
}
/**
* Return true if this table is partitioned.
*/
public boolean isPartitioned() {
return partitionMeta != null;
}
/**
* Return the partition meta for this table.
*/
public PartitionMeta getPartitionMeta() {
return partitionMeta;
}
public void setPkName(String pkName) {
this.pkName = pkName;
}
@@ -531,6 +553,17 @@ public class MTable {
return pk;
}
/**
* Return the primary key column if it is a simple primary key.
*/
public String singlePrimaryKey() {
List<MColumn> columns = primaryKeyColumns();
if (columns.size() == 1) {
return columns.get(0).getName();
}
return null;
}
private void checkTableName(String tableName) {
if (!name.equals(tableName)) {
throw new IllegalArgumentException("addColumn tableName [" + tableName + "] does not match [" + name + "]");
@@ -745,4 +778,8 @@ public class MTable {
compoundKey.setIndexName(null);
}
}
public void setPartitionMeta(PartitionMeta partitionMeta) {
this.partitionMeta = partitionMeta;
}
}
@@ -42,9 +42,10 @@ public class MigrationModel {
// find all the migration xml files
File[] xmlFiles = modelDirectory.listFiles(pathname -> pathname.getName().toLowerCase().endsWith(modelSuffix));
if (xmlFiles == null || xmlFiles.length == 0) {
return;
}
List<MigrationResource> resources = new ArrayList<>(xmlFiles.length);
for (File xmlFile : xmlFiles) {
resources.add(new MigrationResource(xmlFile, createVersion(xmlFile)));
}
@@ -19,6 +19,7 @@ import io.ebeaninternal.dbmigration.migration.DropTable;
import io.ebeaninternal.dbmigration.migration.Migration;
import io.ebeaninternal.dbmigration.migration.Sql;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -43,9 +44,25 @@ public class ModelContainer {
private final PendingDrops pendingDrops = new PendingDrops();
private final List<MTable> partitionedTables = new ArrayList<>();
public ModelContainer() {
}
/**
* Return true if the model contains tables that are partitioned.
*/
public boolean isTablePartitioning() {
return !partitionedTables.isEmpty();
}
/**
* Return the list of partitioned tables.
*/
public List<MTable> getPartitionedTables() {
return partitionedTables;
}
/**
* Adjust the FK references on all the draft tables.
*/
@@ -300,6 +317,9 @@ public class ModelContainer {
* Add a table (typically from reading EbeanServer meta data).
*/
public MTable addTable(MTable table) {
if (table.isPartitioned()) {
partitionedTables.add(table);
}
return tables.put(table.getName(), table);
}
@@ -36,6 +36,7 @@ public class ModelBuildBeanVisitor implements BeanVisitor {
}
MTable table = new MTable(descriptor.getBaseTable());
table.setPartitionMeta(descriptor.getPartitionMeta());
table.setComment(descriptor.getDbComment());
if (descriptor.isHistorySupport()) {
table.setWithHistory(true);
@@ -192,7 +192,7 @@ public class ModelBuildPropertyVisitor extends BaseTablePropertyVisitor {
col.setDbMigrationInfos(p.getDbMigrationInfos());
col.setDefaultValue(p.getDbColumnDefault());
if (columns.length == 1) {
if (p.hasForeignKey()) {
if (p.hasForeignKey() && !importedProperty.getBeanDescriptor().suppressForeignKey()) {
// single references column (put it on the column)
String refTable = importedProperty.getBeanDescriptor().getBaseTable();
if (refTable == null) {
@@ -251,7 +251,7 @@ public class ModelBuildPropertyVisitor extends BaseTablePropertyVisitor {
col.setIdentity(true);
}
TableJoin primaryKeyJoin = p.getBeanDescriptor().getPrimaryKeyJoin();
if (primaryKeyJoin != null) {
if (primaryKeyJoin != null && !table.isPartitioned()) {
TableJoinColumn[] columns = primaryKeyJoin.columns();
col.setReferences(primaryKeyJoin.getTable() + "." + columns[0].getForeignDbColumn());
col.setForeignKeyName(determineForeignKeyConstraintName(col.getName()));
@@ -26,9 +26,22 @@ public class ExtraDdlXmlReader {
public static String buildExtra(String platformName, boolean drops) {
ExtraDdl read = ExtraDdlXmlReader.read("/extra-ddl.xml");
return buildExtra(platformName, drops, read);
}
/**
* Return any extra DDL for supporting partitioning given the database platform.
*/
public static String buildPartitioning(String platformName) {
return buildExtra(platformName, false, readBuiltinTablePartitioning());
}
private static String buildExtra(String platformName, boolean drops, ExtraDdl read) {
if (read == null) {
return null;
}
StringBuilder sb = new StringBuilder(300);
for (DdlScript script : read.getDdlScript()) {
if (script.isDrop() == drops && matchPlatform(platformName, script.getPlatforms())) {
@@ -90,6 +103,13 @@ public class ExtraDdlXmlReader {
return read("/io/ebeaninternal/dbmigration/builtin-extra-ddl.xml");
}
/**
* Read the builtin extra ddl to support table partitioning.
*/
public static ExtraDdl readBuiltinTablePartitioning() {
return read("/io/ebeaninternal/dbmigration/builtin-extra-ddl-partitioning.xml");
}
/**
* Read the extra ddl.
*/
@@ -7,6 +7,7 @@ import io.ebean.SqlUpdate;
import io.ebean.Transaction;
import io.ebean.ValuePair;
import io.ebean.annotation.DocStoreMode;
import io.ebean.annotation.PartitionMode;
import io.ebean.bean.BeanCollection;
import io.ebean.bean.EntityBean;
import io.ebean.bean.EntityBeanIntercept;
@@ -204,6 +205,8 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
private final String draftTable;
private final PartitionMeta partitionMeta;
/**
* DB table comment.
*/
@@ -482,6 +485,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
this.baseTableVersionsBetween = deploy.getBaseTableVersionsBetween();
this.dependentTables = deploy.getDependentTables();
this.dbComment = deploy.getDbComment();
this.partitionMeta = deploy.getPartitionMeta();
this.autoTunable = EntityType.ORM == entityType && (beanFinder == null);
// helper object used to derive lists of properties
@@ -2816,6 +2820,20 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
return dbComment;
}
/**
* Return true if foreign keys to the base table should be suppressed.
*/
public boolean suppressForeignKey() {
return partitionMeta != null;
}
/**
* Return the partition details of the bean.
*/
public PartitionMeta getPartitionMeta() {
return partitionMeta;
}
/**
* Return the dependent tables for a view based entity.
* <p>
@@ -3160,7 +3178,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
public boolean hasIdPropertyOnly(EntityBeanIntercept ebi) {
return ebi.hasIdOnly(idPropertyIndex);
}
public boolean isIdLoaded(EntityBeanIntercept ebi) {
return ebi.isLoadedProperty(idPropertyIndex);
}
@@ -213,6 +213,12 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> implements ST
// by default not including "Many" properties in document store
}
@Override
public void registerColumn(BeanDescriptor<?> desc, String prefix) {
if (targetDescriptor != null) {
desc.registerTable(targetDescriptor.getBaseTable(), this);
}
}
/**
* Return the underlying collection of beans.
*/
@@ -0,0 +1,27 @@
package io.ebeaninternal.server.deploy;
import io.ebean.annotation.PartitionMode;
public class PartitionMeta {
private final PartitionMode mode;
private String property;
public PartitionMeta(PartitionMode mode, String property) {
this.mode = mode;
this.property = property;
}
public PartitionMode getMode() {
return mode;
}
public String getProperty() {
return property;
}
public void setProperty(String dbColumn) {
this.property = dbColumn;
}
}
@@ -3,6 +3,7 @@ package io.ebeaninternal.server.deploy.meta;
import io.ebean.annotation.Cache;
import io.ebean.annotation.DocStore;
import io.ebean.annotation.DocStoreMode;
import io.ebean.annotation.PartitionMode;
import io.ebean.config.ServerConfig;
import io.ebean.config.TableName;
import io.ebean.config.dbplatform.IdType;
@@ -27,6 +28,7 @@ import io.ebeaninternal.server.deploy.ChainedBeanQueryAdapter;
import io.ebeaninternal.server.deploy.DeployPropertyParserMap;
import io.ebeaninternal.server.deploy.IndexDefinition;
import io.ebeaninternal.server.deploy.InheritInfo;
import io.ebeaninternal.server.deploy.PartitionMeta;
import io.ebeaninternal.server.deploy.TableJoin;
import io.ebeaninternal.server.deploy.parse.DeployBeanInfo;
import io.ebeaninternal.server.idgen.UuidV1IdGenerator;
@@ -193,6 +195,8 @@ public class DeployBeanDescriptor<T> {
private String dbComment;
private PartitionMeta partitionMeta;
/**
* One of NONE, INDEX or EMBEDDED.
*/
@@ -305,6 +309,20 @@ public class DeployBeanDescriptor<T> {
return dbComment;
}
public void setPartitionMeta(PartitionMeta partitionMeta) {
this.partitionMeta = partitionMeta;
}
public PartitionMeta getPartitionMeta() {
if (partitionMeta != null) {
DeployBeanProperty beanProperty = getBeanProperty(partitionMeta.getProperty());
if (beanProperty != null) {
partitionMeta.setProperty(beanProperty.getDbColumn());
}
}
return partitionMeta;
}
public void setDraftable() {
draftable = true;
}
@@ -304,6 +304,12 @@ class AnnotationAssocManys extends AnnotationParser {
private void readJoinTable(JoinTable joinTable, DeployBeanPropertyAssocMany<?> prop) {
String intTableName = getFullTableName(joinTable);
if (intTableName.isEmpty()) {
BeanTable localTable = factory.getBeanTable(descriptor.getBeanType());
BeanTable otherTable = factory.getBeanTable(prop.getTargetType());
intTableName = getM2MJoinTableName(localTable, otherTable);
}
// set the intersection table
DeployTableJoin intJoin = new DeployTableJoin();
intJoin.setTable(intTableName);
@@ -2,6 +2,7 @@ package io.ebeaninternal.server.deploy.parse;
import io.ebean.annotation.Cache;
import io.ebean.annotation.DbComment;
import io.ebean.annotation.DbPartition;
import io.ebean.annotation.DocStore;
import io.ebean.annotation.Draftable;
import io.ebean.annotation.DraftableElement;
@@ -16,6 +17,7 @@ import io.ebean.util.AnnotationUtil;
import io.ebeaninternal.server.deploy.BeanDescriptor.EntityType;
import io.ebeaninternal.server.deploy.IndexDefinition;
import io.ebeaninternal.server.deploy.InheritInfo;
import io.ebeaninternal.server.deploy.PartitionMeta;
import io.ebeaninternal.server.deploy.meta.DeployBeanProperty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -152,6 +154,11 @@ public class AnnotationClass extends AnnotationParser {
}
}
DbPartition partition = AnnotationUtil.findAnnotationRecursive(cls, DbPartition.class);
if (partition != null) {
descriptor.setPartitionMeta(new PartitionMeta(partition.mode(), partition.property()));
}
Draftable draftable = AnnotationUtil.findAnnotationRecursive(cls, Draftable.class);
if (draftable != null) {
descriptor.setDraftable();
@@ -6,6 +6,7 @@ import io.ebean.DtoQuery;
import io.ebean.Expression;
import io.ebean.ExpressionFactory;
import io.ebean.ExpressionList;
import io.ebean.FetchGroup;
import io.ebean.FetchPath;
import io.ebean.FutureIds;
import io.ebean.FutureList;
@@ -460,6 +461,11 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
return query.select(fetchProperties);
}
@Override
public Query<T> select(FetchGroup fetchGroup) {
return query.select(fetchGroup);
}
@Override
public Query<T> setDistinct(boolean distinct) {
return query.setDistinct(distinct);
@@ -5,6 +5,7 @@ import io.ebean.CountDistinctOrder;
import io.ebean.DtoQuery;
import io.ebean.Expression;
import io.ebean.ExpressionList;
import io.ebean.FetchGroup;
import io.ebean.FetchPath;
import io.ebean.FutureIds;
import io.ebean.FutureList;
@@ -759,6 +760,11 @@ class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, Expression
return exprList.select(properties);
}
@Override
public Query<T> select(FetchGroup fetchGroup) {
return exprList.select(fetchGroup);
}
@Override
public Query<T> setDistinct(boolean distinct) {
return exprList.setDistinct(distinct);
@@ -0,0 +1,26 @@
package io.ebeaninternal.server.query;
import io.ebeaninternal.server.querydefn.OrmQueryDetail;
import io.ebeaninternal.server.querydefn.SpiFetchGroup;
/**
* Default FetchGroup implementation.
*/
class DFetchGroup<T> implements SpiFetchGroup<T> {
private final OrmQueryDetail detail;
DFetchGroup(OrmQueryDetail detail) {
this.detail = detail;
}
@Override
public OrmQueryDetail detail() {
return detail.copy();
}
@Override
public OrmQueryDetail underlying() {
return detail;
}
}
@@ -0,0 +1,92 @@
package io.ebeaninternal.server.query;
import io.ebean.FetchConfig;
import io.ebean.FetchGroup;
import io.ebean.FetchGroupBuilder;
import io.ebeaninternal.server.querydefn.OrmQueryDetail;
import io.ebeaninternal.server.querydefn.SpiFetchGroup;
/**
* Default implementation of the FetchGroupBuilder.
*/
class DFetchGroupBuilder<T> implements FetchGroupBuilder<T> {
private static final FetchConfig FETCH_QUERY = new FetchConfig().query();
private static final FetchConfig FETCH_LAZY = new FetchConfig().lazy();
private final OrmQueryDetail detail;
DFetchGroupBuilder() {
this.detail = new OrmQueryDetail();
}
@Override
public FetchGroupBuilder<T> select(String select) {
detail.select(select);
return this;
}
@Override
public FetchGroupBuilder<T> fetch(String path) {
detail.fetch(path, null, null);
return this;
}
@Override
public FetchGroupBuilder<T> fetch(String path, FetchGroup nestedGroup) {
return fetchNested(path, nestedGroup, null);
}
@Override
public FetchGroupBuilder<T> fetchQuery(String path, FetchGroup nestedGroup) {
return fetchNested(path, nestedGroup, FETCH_QUERY);
}
@Override
public FetchGroupBuilder<T> fetchLazy(String path, FetchGroup nestedGroup) {
return fetchNested(path, nestedGroup, FETCH_LAZY);
}
private FetchGroupBuilder<T> fetchNested(String path, FetchGroup nestedGroup, FetchConfig fetchConfig) {
OrmQueryDetail nestedDetail = ((SpiFetchGroup) nestedGroup).underlying();
detail.addNested(path, nestedDetail, fetchConfig);
return this;
}
@Override
public FetchGroupBuilder<T> fetchQuery(String path) {
detail.fetch(path, null, FETCH_QUERY);
return this;
}
@Override
public FetchGroupBuilder<T> fetchLazy(String path) {
detail.fetch(path, null, FETCH_LAZY);
return this;
}
@Override
public FetchGroupBuilder<T> fetch(String path, String properties) {
detail.fetch(path, properties, null);
return this;
}
@Override
public FetchGroupBuilder<T> fetchQuery(String path, String properties) {
detail.fetch(path, properties, FETCH_QUERY);
return this;
}
@Override
public FetchGroupBuilder<T> fetchLazy(String path, String properties) {
detail.fetch(path, properties, FETCH_LAZY);
return this;
}
@Override
public FetchGroup<T> build() {
return new DFetchGroup<>(detail);
}
}
@@ -0,0 +1,28 @@
package io.ebeaninternal.server.query;
import io.ebean.FetchGroup;
import io.ebean.FetchGroupBuilder;
import io.ebean.service.SpiFetchGroupService;
import io.ebeaninternal.server.querydefn.OrmQueryDetail;
/**
* Default implementation of SpiFetchGroupService.
*/
public final class DFetchGroupService implements SpiFetchGroupService {
@Override
public <T> FetchGroup<T> of(Class<T> cls, String select) {
return new DFetchGroup<>(detail(select));
}
@Override
public <T> FetchGroupBuilder<T> of(Class<T> cls) {
return new DFetchGroupBuilder<>();
}
private OrmQueryDetail detail(String select) {
OrmQueryDetail detail = new OrmQueryDetail();
detail.select(select);
return detail;
}
}
@@ -412,7 +412,13 @@ public final class SqlTreeBuilder {
if (!selectProps.containsProperty(baseName)) {
STreeProperty p = desc.findPropertyWithDynamic(baseName);
if (p == null) {
logger.error("property [" + propName + "] not found on " + desc + " for query - excluding it.");
// maybe dynamic formula with schema prefix
p = desc.findPropertyWithDynamic(propName);
if (p != null) {
selectProps.add(p);
} else {
logger.error("property [" + propName + "] not found on " + desc + " for query - excluding it.");
}
} else if (p.isEmbedded()) {
// add the embedded bean (and effectively
@@ -7,6 +7,7 @@ import io.ebean.Expression;
import io.ebean.ExpressionFactory;
import io.ebean.ExpressionList;
import io.ebean.FetchConfig;
import io.ebean.FetchGroup;
import io.ebean.FetchPath;
import io.ebean.FutureIds;
import io.ebean.FutureList;
@@ -1329,6 +1330,12 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
return this;
}
@Override
public DefaultOrmQuery<T> select(FetchGroup fetchGroup) {
this.detail = ((SpiFetchGroup)fetchGroup).detail();
return this;
}
@Override
public DefaultOrmQuery<T> fetch(String property) {
return fetch(property, null, null);
@@ -55,6 +55,16 @@ public class OrmQueryDetail implements Serializable {
return copy;
}
/**
* Add a nested OrmQueryDetail to this detail.
*/
public void addNested(String path, OrmQueryDetail other, FetchConfig config) {
fetch(path, other.baseProps.getProperties(), config);
for (Map.Entry<String, OrmQueryProperties> entry : other.fetchPaths.entrySet()) {
fetch(path + "." + entry.getKey(), entry.getValue().getProperties(), entry.getValue().getFetchConfig());
}
}
/**
* Calculate the hash for the query plan.
*/
@@ -148,27 +148,30 @@ public class OrmQueryProperties implements Serializable {
/**
* Copy constructor.
*/
private OrmQueryProperties(OrmQueryProperties source) {
private OrmQueryProperties(OrmQueryProperties source, FetchConfig sourceFetchConfig) {
this.fetchConfig = sourceFetchConfig;
this.parentPath = source.parentPath;
this.path = source.path;
this.rawProperties = source.rawProperties;
this.trimmedProperties = source.trimmedProperties;
this.cache = source.cache;
this.readOnly = source.readOnly;
this.fetchConfig = source.fetchConfig;
this.filterMany = source.filterMany;
this.included = (source.included == null) ? null : new LinkedHashSet<>(source.included);
if (includedBeanJoin != null) {
this.includedBeanJoin = new HashSet<>(source.includedBeanJoin);
}
}
/**
* Creates a copy of the OrmQueryProperties.
*/
public OrmQueryProperties copy() {
return new OrmQueryProperties(this);
return new OrmQueryProperties(this, this.fetchConfig);
}
/**
* Create a copy with the given fetch config.
*/
public OrmQueryProperties copy(FetchConfig fetchConfig) {
return new OrmQueryProperties(this, fetchConfig);
}
/**
@@ -0,0 +1,19 @@
package io.ebeaninternal.server.querydefn;
import io.ebean.FetchGroup;
/**
* Service API of FetchGroup.
*/
public interface SpiFetchGroup<T> extends FetchGroup<T> {
/**
* Return the detail to use for query execution.
*/
OrmQueryDetail detail();
/**
* Return the underlying detail for copy purposes.
*/
OrmQueryDetail underlying();
}
@@ -301,7 +301,7 @@ public class TransactionManager implements SpiTransactionManager {
}
// default to rollback if not defined on the platform
return dbPlatformOnQueryOnly == null ? OnQueryOnly.ROLLBACK : dbPlatformOnQueryOnly;
return dbPlatformOnQueryOnly == null ? OnQueryOnly.COMMIT : dbPlatformOnQueryOnly;
}
public String getServerName() {
@@ -112,14 +112,14 @@ interface ArrayElementConverter<T> {
@Override
public Object toElement(Object rawValue) {
return scalarType.parse(rawValue.toString());
return scalarType.toBeanType(rawValue);
}
@Override
public Object[] toDbArray(Object[] objects) {
Object[] dbArray = new Object[objects.length];
for (int i = 0; i < objects.length; i++) {
dbArray[i] = scalarType.format(objects[i]);
dbArray[i] = scalarType.toJdbcType(objects[i]);
}
return dbArray;
}
@@ -60,7 +60,19 @@ public class ScalarTypeArrayList extends ScalarTypeJsonCollection<List> implemen
@Override
public ScalarTypeArrayList typeForEnum(ScalarType<?> scalarType) {
return new ScalarTypeArrayList("varchar", DocPropertyType.TEXT, new ArrayElementConverter.EnumConverter(scalarType));
final String arrayType;
switch (scalarType.getJdbcType()) {
case Types.INTEGER:
arrayType = "integer";
break;
case Types.VARCHAR:
arrayType = "varchar";
break;
default:
throw new IllegalArgumentException("JdbcType [" + scalarType.getJdbcType() + "] not supported for @DbArray mapping on set.");
}
return new ScalarTypeArrayList(arrayType, scalarType.getDocType(), new ArrayElementConverter.EnumConverter(scalarType));
}
}
@@ -54,13 +54,24 @@ public class ScalarTypeArraySet<T> extends ScalarTypeJsonCollection<Set<T>> impl
if (valueType.equals(String.class)) {
return STRING;
}
throw new IllegalArgumentException("Type [" + valueType + "] not supported for @DbArray mapping on set");
throw new IllegalArgumentException("Type [" + valueType + "] not supported for @DbArray mapping on list");
}
@Override
@SuppressWarnings({ "unchecked", "rawtypes" })
@SuppressWarnings({"unchecked", "rawtypes"})
public ScalarTypeArraySet typeForEnum(ScalarType<?> scalarType) {
return new ScalarTypeArraySet("varchar", DocPropertyType.TEXT, new ArrayElementConverter.EnumConverter(scalarType));
final String arrayType;
switch (scalarType.getJdbcType()) {
case Types.INTEGER:
arrayType = "integer";
break;
case Types.VARCHAR:
arrayType = "varchar";
break;
default:
throw new IllegalArgumentException("JdbcType [" + scalarType.getJdbcType() + "] not supported for @DbArray mapping on set.");
}
return new ScalarTypeArraySet(arrayType, scalarType.getDocType(), new ArrayElementConverter.EnumConverter(scalarType));
}
}
@@ -68,9 +79,9 @@ public class ScalarTypeArraySet<T> extends ScalarTypeJsonCollection<Set<T>> impl
private final ArrayElementConverter<T> converter;
@SuppressWarnings({ "unchecked", "rawtypes" })
@SuppressWarnings({"unchecked", "rawtypes"})
public ScalarTypeArraySet(String arrayType, DocPropertyType docPropertyType, ArrayElementConverter<T> converter) {
super((Class)Set.class, Types.ARRAY, docPropertyType);
super((Class) Set.class, Types.ARRAY, docPropertyType);
this.arrayType = arrayType;
this.converter = converter;
}
@@ -7,6 +7,7 @@ import java.sql.SQLException;
import java.sql.Types;
import java.util.Set;
import java.util.UUID;
/**
* H2 database support for DB ARRAY.
*/
@@ -48,13 +49,13 @@ class ScalarTypeArraySetH2<T> extends ScalarTypeArraySet<T> {
}
@Override
@SuppressWarnings({ "unchecked", "rawtypes" })
@SuppressWarnings({"unchecked", "rawtypes"})
public ScalarTypeArraySetH2 typeForEnum(ScalarType<?> scalarType) {
return new ScalarTypeArraySetH2("varchar", DocPropertyType.TEXT, new ArrayElementConverter.EnumConverter(scalarType));
}
}
@SuppressWarnings({ "unchecked", "rawtypes" })
@SuppressWarnings({"unchecked", "rawtypes"})
private ScalarTypeArraySetH2(String arrayType, DocPropertyType docPropertyType, ArrayElementConverter converter) {
super(arrayType, docPropertyType, converter);
}
@@ -0,0 +1 @@
io.ebeaninternal.server.query.DFetchGroupService
+36 -4
View File
@@ -37,9 +37,17 @@
<xsd:enumeration value="apply"/>
<xsd:enumeration value="pendingDrops"/>
<xsd:enumeration value="baseline"/>
<xsd:enumeration value="drop"/>
</xsd:restriction>
</xsd:simpleType>
<xsd:complexType name="ddl-script">
<xsd:sequence>
<xsd:element name="ddl" type="xsd:string" maxOccurs="unbounded"/>
</xsd:sequence>
<xsd:attribute name="platforms" type="xsd:string"/>
</xsd:complexType>
<!-- =========================================================== -->
<!-- CHANGE SET CHILDREN -->
<!-- =========================================================== -->
@@ -99,6 +107,8 @@
<xsd:element ref="foreignKey" minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required"/>
<xsd:attribute name="partitionMode" type="xsd:string"/>
<xsd:attribute name="partitionColumn" type="xsd:string"/>
<xsd:attribute name="withHistory" type="xsd:boolean"/>
<xsd:attribute name="draft" type="xsd:boolean"/>
<xsd:attribute name="identityType" type="identityType"/>
@@ -125,6 +135,18 @@
<xsd:complexType>
<xsd:attribute name="name" type="xsd:string" use="required"/>
<xsd:attribute name="columnNames" type="xsd:string" use="required"/>
<xsd:attribute name="oneToOne" type="xsd:boolean"/>
<xsd:attribute name="nullableColumns" type="xsd:string"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="addUniqueConstraint">
<xsd:complexType>
<xsd:attribute name="constraintName" type="xsd:string" use="required"/>
<xsd:attribute name="tableName" type="xsd:string" use="required"/>
<xsd:attribute name="columnNames" type="xsd:string" use="required"/>
<xsd:attribute name="oneToOne" type="xsd:boolean"/>
<xsd:attribute name="nullableColumns" type="xsd:string"/>
</xsd:complexType>
</xsd:element>
@@ -157,6 +179,8 @@
<xsd:element name="dropTable">
<xsd:complexType>
<xsd:attribute name="name" type="xsd:string" use="required"/>
<xsd:attribute name="sequenceCol" type="xsd:string"/>
<xsd:attribute name="sequenceName" type="xsd:string"/>
</xsd:complexType>
</xsd:element>
@@ -216,6 +240,10 @@
<xsd:element name="alterColumn">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="before" minOccurs="0" maxOccurs="unbounded" type="ddl-script"/>
<xsd:element name="after" minOccurs="0" maxOccurs="unbounded" type="ddl-script"/>
</xsd:sequence>
<xsd:attribute name="columnName" type="xsd:string" use="required"/>
<xsd:attribute name="tableName" type="xsd:string" use="required"/>
<xsd:attribute name="withHistory" type="xsd:boolean"/>
@@ -236,8 +264,8 @@
<xsd:attribute name="references" type="xsd:string"/>
<xsd:attribute name="foreignKeyName" type="xsd:string"/>
<xsd:attribute name="foreignKeyIndex" type="xsd:string"/>
<xsd:attribute name="foreignOnDelete" type="xsd:string"/>
<xsd:attribute name="foreignOnUpdate" type="xsd:string"/>
<xsd:attribute name="foreignKeyOnDelete" type="xsd:string"/>
<xsd:attribute name="foreignKeyOnUpdate" type="xsd:string"/>
<xsd:attribute name="dropForeignKey" type="xsd:string"/>
<xsd:attribute name="dropForeignKeyIndex" type="xsd:string"/>
</xsd:complexType>
@@ -269,11 +297,14 @@
</xsd:complexType>
</xsd:element>
<!-- ============================================ -->
<xsd:element name="column">
<xsd:complexType mixed="true">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="before" minOccurs="0" maxOccurs="unbounded" type="ddl-script"/>
<xsd:element name="after" minOccurs="0" maxOccurs="unbounded" type="ddl-script"/>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required"/>
<xsd:attribute name="type" type="xsd:string" use="required"/>
<xsd:attribute name="defaultValue" type="xsd:string"/>
@@ -310,6 +341,7 @@
<xsd:element ref="dropTable" maxOccurs="unbounded"/>
<xsd:element ref="renameTable" maxOccurs="unbounded"/>
<xsd:element ref="addTableComment" maxOccurs="unbounded"/>
<xsd:element ref="addUniqueConstraint" maxOccurs="unbounded"/>
<xsd:element ref="addHistoryTable" maxOccurs="unbounded"/>
<xsd:element ref="dropHistoryTable" maxOccurs="unbounded"/>
@@ -18,6 +18,8 @@
<xsd:extension base="xsd:string">
<xsd:attribute name="name" type="xsd:string" use="required"/>
<xsd:attribute name="platforms" type="xsd:string"/>
<xsd:attribute name="init" type="xsd:boolean"/>
<xsd:attribute name="drop" type="xsd:boolean"/>
</xsd:extension>
</xsd:simpleContent>
</xsd:complexType>
@@ -0,0 +1,242 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<extra-ddl xmlns="http://ebean-orm.github.io/xml/ns/extraddl">
<ddl-script name="partition help" init="true" platforms="postgres">
-- partitioning helper functions (UTC based)
------------------------------------------------------------------------------------
-- Type: partition_meta
--
-- Type used to hold common partitioning parameters such as period start and end etc
------------------------------------------------------------------------------------
do $$
begin
if not exists (select 1 from pg_type where typname = 'partition_meta') THEN
create type partition_meta as
(
period_start timestamptz,
period_end timestamptz,
period_name text,
base_name text,
part_name text,
unique_column text,
index_column text
);
end if;
end$$;
------------------------------------------------------------------------------------
-- Function: _partition_create
--
-- Internal helper method to create a partition given meta data and
-- optional extra function to call (typically to create additional indexes)
------------------------------------------------------------------------------------
create or replace function _partition_create(meta partition_meta, extra text)
returns text
language plpgsql
set timezone to 'UTC'
as $$
begin
execute format('create table if not exists %I partition of %I for values from (''%s'') TO (''%s'')', meta.part_name, meta.base_name, meta.period_start, meta.period_end);
if (length(meta.unique_column) > 0) then
execute format('create unique index if not exists uq_%I ON %I (%I)', meta.part_name, meta.part_name, meta.unique_column);
end if;
if (length(meta.index_column) > 0) then
execute format('create index if not exists ix_%I_%s ON %I (%I)', meta.part_name, meta.index_column, meta.part_name, meta.index_column);
end if;
if (length(extra) > 0) then
execute 'select ' || extra || '($1)' using meta;
end if;
return meta.part_name;
end;
$$;
------------------------------------------------------------------------------------
-- Function: _partition_meta
--
-- Internal helper method to create and return meta data used to create a partition.
-- Helps work out start and end periods for day, week, month and year partitions.
------------------------------------------------------------------------------------
create or replace function _partition_meta(
mode text,
asOf date,
baseName text,
uniqueColumn text,
indexColumn text)
returns partition_meta
language plpgsql
set timezone to 'UTC'
as $$
declare
partName text;
meta partition_meta;
asOfUtc timestamptz;
begin
asOfUtc = timezone('utc', asOf);
if (mode = 'day') then
asOfUtc = date_trunc('day', asOfUtc);
partName = to_char(asOfUtc, 'YYYY_MM_DD');
select asOfUtc, asOfUtc + interval '1 days' into meta.period_start, meta.period_end;
elseif (mode = 'week') then
asOfUtc = date_trunc('week', asOfUtc);
partName = format('%s_w%s', extract(ISOYEAR FROM asOfUtc), extract(WEEK FROM asOfUtc));
select asOfUtc, asOfUtc + interval '7 days' into meta.period_start, meta.period_end;
elseif (mode = 'year') then
asOfUtc = date_trunc('year', asOfUtc);
partName = to_char(date_trunc('year', asOfUtc), 'YYYY');
select asOfUtc, asOfUtc + interval '1 year' into meta.period_start, meta.period_end;
else
asOfUtc = date_trunc('month', asOfUtc);
partName = to_char(asOfUtc, 'YYYY_MM');
select asOfUtc, asOfUtc + interval '1 month' into meta.period_start, meta.period_end;
end if;
select partName, baseName, format('%s_%s', baseName, partName), uniqueColumn, indexColumn
into meta.period_name, meta.base_name, meta.part_name, meta.unique_column, meta.index_column;
return meta;
end;
$$;
create or replace function _partition_meta_initdate(
meta partition_meta,
initDate date)
returns partition_meta
language plpgsql
set timezone to 'UTC'
as $$
begin
meta.period_start = initDate;
return meta;
end;
$$;
-- select _partition_over('week', current_date, 4);
------------------------------------------------------------------------------------
-- Function: _partition_over
--
-- Internal helper method to return a set/table of dates to ensure partitions exists for.
-- Typically we want to ensure some future partitions exist and this helps return dates
-- for which we loop to create partitions.
------------------------------------------------------------------------------------
create or replace function _partition_over(
mode text,
fromDate date default current_date,
_count integer default 0)
returns TABLE(of_date date)
language plpgsql
as $$
declare
endDate date;
begin
if (mode = 'day') then
endDate = fromDate + (interval '1 day' * _count);
fromDate = fromDate - interval '1 day'; -- allow for timezone
return query select s::date from generate_series(fromDate, endDate, '1 day') s;
elseif (mode = 'week') then
fromDate = date_trunc('week', fromDate);
endDate = fromDate + (interval '1 week' * _count);
return query select s::date from generate_series(fromDate, endDate, '1 week') s;
elseif (mode = 'year') then
fromDate = date_trunc('year', fromDate);
endDate = fromDate + (interval '1 year' * _count);
return query select s::date from generate_series(fromDate, endDate, '1 year') s;
else
fromDate = date_trunc('month', fromDate);
endDate = fromDate + (interval '1 month' * _count);
return query select s::date from generate_series(fromDate, endDate, '1 month') s;
end if;
end;
$$;
------------------------------------------------------------------------------------
-- Function: partition
--
-- Helper to ensure we create partitions into the future as needed for day, week, month
-- and year based partitioning. Typically we call this periodically (e.g. every day).
--
-- Examples:
--
-- select partition('week', 'trip', 'id', 'when_started', 4);
-- select partition('month', 'event', 'id', 'event_timestamp', 1);
--
------------------------------------------------------------------------------------
create or replace function partition(
mode text, -- one of 'day','week','month','year'
baseName text, -- base table name
uniqueColumn text, -- optional unique column
indexColumn text, -- optional column to index
partitionCount integer default 0, -- number of additional partitions
fromDate date default current_date, -- date to create first partition for
extra text default '') -- custom function to call per partition
returns text
language plpgsql
set timezone to 'UTC'
as $$
begin
perform _partition_create(_partition_meta(mode, poDate, baseName, uniqueColumn, indexColumn), extra)
from _partition_over(mode, fromDate, partitionCount) poDate;
return 'done';
end;
$$;
------------------------------------------------------------------------------------
-- Function: partition_init
--
-- Similar to partition but allows the first partition to be bigger with an explicit
-- initDate typically to allow back dated rows to go into the initial partition.
--
-- Examples:
--
-- select partition_init(date '2001-01-01', 'week', 'event', 'id', 'event_timestamp', 4);
--
------------------------------------------------------------------------------------
create or replace function partition_init(
initDate date, -- first partition period start date
mode text, -- one of 'day','week','month','year'
baseName text, -- base table name
uniqueColumn text, -- optional unique column
indexColumn text, -- optional column to index
partitionCount integer default 0, -- number of additional partitions
fromDate date default current_date, -- date to create first partition for
extra text default '') -- custom function to call per partition
returns text
language plpgsql
set timezone to 'UTC'
as $$
declare
meta partition_meta;
begin
-- override the period start for the first partition
meta = _partition_meta(mode, fromDate, baseName, uniqueColumn, indexColumn);
meta = _partition_meta_initdate(meta, initDate);
perform _partition_create(meta, extra);
if (partitionCount > 0) then
-- create additional migrations normally
perform _partition_create(_partition_meta(mode, poDate, baseName, uniqueColumn, indexColumn), extra)
from _partition_over(mode, fromDate, partitionCount) poDate;
end if;
return 'done';
end;
$$;
</ddl-script>
</extra-ddl>
+115
View File
@@ -0,0 +1,115 @@
package io.ebean;
import org.ebeantest.LoggedSqlCollector;
import org.junit.Test;
import org.tests.model.basic.Address;
import org.tests.model.basic.Contact;
import org.tests.model.basic.Customer;
import org.tests.model.basic.ResetBasicData;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
public class FetchGroupTest extends BaseTestCase {
@Test
public void simple() {
FetchGroup<Customer> fetch = FetchGroup.of(Customer.class, "name, status");
Query<Customer> query = Customer.find
.query()
.where()
.ilike("name", "rob")
.select(fetch);
query.findList();
assertThat(sqlOf(query)).contains("select t0.id, t0.name, t0.status from");
}
@Test
public void nestedWithQueryJoin() {
ResetBasicData.reset();
FetchGroup<Customer> fetch = FetchGroup.of(Customer.class)
.select("name, status")
.fetchQuery("contacts", "firstName, lastName, email")
.build();
Query<Customer> query = Customer.find
.query()
.where()
.ilike("name", "rob")
.select(fetch);
LoggedSqlCollector.start();
query.findList();
List<String> sql = LoggedSqlCollector.stop();
assertThat(sql).hasSize(2);
assertThat(sql.get(0)).contains("select t0.id, t0.name, t0.status from o_customer");
assertThat(sql.get(1)).contains("select t0.customer_id, t0.id, t0.first_name, t0.last_name, t0.email from contact");
}
@Test
public void nestedWithQueryJoin_asNestedFetchGroup() {
ResetBasicData.reset();
FetchGroup<Contact> CT_NAME = FetchGroup.of(Contact.class, "firstName, lastName, email");
FetchGroup<Customer> fetch = FetchGroup.of(Customer.class)
.select("name")
.fetchQuery("contacts", CT_NAME)
.build();
Query<Customer> query = Customer.find
.query()
.where()
.ilike("name", "rob")
.select(fetch);
LoggedSqlCollector.start();
query.findList();
List<String> sql = LoggedSqlCollector.stop();
assertThat(sql).hasSize(2);
assertThat(sql.get(0)).contains("select t0.id, t0.name from o_customer");
assertThat(sql.get(1)).contains(" from contact");
}
@Test
public void nested_withNestedFetchGroup() {
ResetBasicData.reset();
FetchGroup<Address> FGAddress = FetchGroup.of(Address.class)
.select("line1, line2, city")
.fetch("country", "name")
.build();
FetchGroup<Customer> FBCustomer = FetchGroup.of(Customer.class)
.select("name, version")
.fetch("billingAddress", FGAddress)
.build();
Query<Customer> query = Customer.find
.query()
.where()
.ilike("name", "rob")
.select(FBCustomer);
LoggedSqlCollector.start();
query.findList();
List<String> sql = LoggedSqlCollector.stop();
assertThat(sql).hasSize(1);
assertThat(sql.get(0)).contains("select t0.id, t0.name, t0.version, t1.id, t1.line_1, t1.line_2, t1.city, t2.code, t2.name from o_customer t0 left join o_address t1 on t1.id = t0.billing_address_id left join o_country t2 on t2.code = t1.country_code ");
}
}
@@ -3,15 +3,14 @@ package org.tests.batchload;
import io.ebean.Ebean;
import io.ebean.FetchConfig;
import io.ebean.Query;
import io.ebean.QueryIterator;
import io.ebean.TransactionalTestCase;
import org.ebeantest.LoggedSqlCollector;
import org.junit.Test;
import org.tests.model.basic.Customer;
import org.tests.model.basic.Order;
import org.tests.model.basic.ResetBasicData;
import org.ebeantest.LoggedSqlCollector;
import org.junit.Test;
import java.util.Iterator;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
@@ -78,13 +77,15 @@ public class TestSecondaryQueries extends TransactionalTestCase {
LoggedSqlCollector.start();
Iterator<Order> orders = Ebean.find(Order.class)
.select("status")
.setMaxRows(10)
.setUseCache(false)
.findIterate();
while (orders.hasNext()) {
orders.next(); // dummy read
try (QueryIterator<Order> orders =
Ebean.find(Order.class).select("status")
.setMaxRows(10)
.setUseCache(false)
.findIterate()) {
while (orders.hasNext()) {
orders.next(); // dummy read
}
}
List<String> sql = LoggedSqlCollector.stop();
@@ -38,6 +38,12 @@ public class EArrayBean {
@DbArray
List<Status> statuses;
@DbArray
List<VarcharEnum> vcEnums = new ArrayList<>();
@DbArray
List<IntEnum> intEnums = new ArrayList<>();
@DbArray
Set<Status> status2;
@@ -100,6 +106,21 @@ public class EArrayBean {
this.statuses = statuses;
}
public List<VarcharEnum> getVcEnums() {
return vcEnums;
}
public void setVcEnums(final List<VarcharEnum> vcEnums) {
this.vcEnums = vcEnums;
}
public List<IntEnum> getIntEnums() {
return intEnums;
}
public void setIntEnums(final List<IntEnum> intEnums) {
this.intEnums = intEnums;
}
public Set<Status> getStatus2() {
return status2;
@@ -0,0 +1,13 @@
package org.tests.model.array;
import io.ebean.annotation.DbEnumType;
import io.ebean.annotation.DbEnumValue;
public enum IntEnum {
ZERO, ONE, TWO;
@DbEnumValue(storage = DbEnumType.INTEGER)
public int dbValue() {
return 100 + ordinal();
}
}
@@ -1,19 +1,22 @@
package org.tests.model.array;
import io.ebean.BaseTestCase;
import io.ebean.Ebean;
import io.ebean.Query;
import org.ebeantest.LoggedSqlCollector;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertEquals;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertEquals;
import org.ebeantest.LoggedSqlCollector;
import org.junit.Test;
import io.ebean.BaseTestCase;
import io.ebean.Ebean;
import io.ebean.Query;
import io.ebean.SqlRow;
public class TestDbArray_basic extends BaseTestCase {
@@ -22,7 +25,7 @@ public class TestDbArray_basic extends BaseTestCase {
private EArrayBean found;
@Test
public void insert() {
public void insert() throws SQLException {
bean.setName("some stuff");
@@ -44,6 +47,10 @@ public class TestDbArray_basic extends BaseTestCase {
bean.setStatuses(new ArrayList<>());
bean.getStatuses().add(EArrayBean.Status.ONE);
bean.getStatuses().add(EArrayBean.Status.THREE);
bean.getVcEnums().add(VarcharEnum.ONE);
bean.getVcEnums().add(VarcharEnum.TWO);
bean.getIntEnums().add(IntEnum.ZERO);
bean.getIntEnums().add(IntEnum.TWO);
bean.setStatus2(new LinkedHashSet<>());
bean.getStatus2().add(EArrayBean.Status.TWO);
@@ -64,16 +71,23 @@ public class TestDbArray_basic extends BaseTestCase {
.arrayIsNotEmpty("phoneNumbers")
.arrayContains("statuses", EArrayBean.Status.ONE)
.arrayContains("status2", EArrayBean.Status.TWO)
.arrayContains("vcEnums", VarcharEnum.TWO)
.arrayContains("intEnums", IntEnum.ZERO)
.query();
List<EArrayBean> list = query.findList();
List<EArrayBean.Status> statuses = list.get(0).getStatuses();
Set<EArrayBean.Status> status2 = list.get(0).getStatus2();
List<IntEnum> intEnums = list.get(0).getIntEnums();
List<VarcharEnum> varcharEnums = list.get(0).getVcEnums();
assertThat(statuses).contains(EArrayBean.Status.ONE, EArrayBean.Status.THREE);
assertThat(status2).contains(EArrayBean.Status.ONE, EArrayBean.Status.TWO);
assertThat(intEnums).containsExactly(IntEnum.ZERO, IntEnum.TWO);
assertThat(varcharEnums).containsExactly(VarcharEnum.ONE, VarcharEnum.TWO);
assertThat(query.getGeneratedSql()).contains(" t0.other_ids @> array[?,?]::bigint[] ");
assertThat(query.getGeneratedSql()).contains(" t0.uids @> array[?] ");
assertThat(query.getGeneratedSql()).contains(" t0.phone_numbers @> array[?] ");
@@ -87,6 +101,15 @@ public class TestDbArray_basic extends BaseTestCase {
.query();
query.findList();
final SqlRow row = Ebean.createSqlQuery("select * from earray_bean").findOne();
final String[] vcs = (String[]) ((java.sql.Array) row.get("vc_enums")).getArray();
assertThat(vcs).containsExactly("xXxONE", "xXxTWO");
final Integer[] ints = (Integer[]) ((java.sql.Array) row.get("int_enums")).getArray();
assertThat(ints).containsExactly(100, 102);
assertThat(query.getGeneratedSql()).contains(" coalesce(cardinality(t0.other_ids),0) = 0");
assertThat(query.getGeneratedSql()).contains(" not (t0.uids @> array[?])");
}
@@ -0,0 +1,13 @@
package org.tests.model.array;
import io.ebean.annotation.DbEnumType;
import io.ebean.annotation.DbEnumValue;
public enum VarcharEnum {
ZERO, ONE, TWO;
@DbEnumValue(storage = DbEnumType.VARCHAR)
public String dbValue() {
return "xXx" + name();
}
}
@@ -0,0 +1,65 @@
package org.tests.o2m.jointable;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.JoinTable;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Version;
import java.util.List;
@Entity
@Table(name="mkeygroup")
public class JtMonkeyGroup {
@Id
long pid;
String name;
/**
* No cascading over to Monkey but we do maintain the join table regardless.
*/
@OneToMany
@JoinTable
List<JtMonkey> monkeys;
@Version
long version;
public JtMonkeyGroup(String name) {
this.name = name;
}
public long getPid() {
return pid;
}
public void setPid(long pid) {
this.pid = pid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<JtMonkey> getMonkeys() {
return monkeys;
}
public void setMonkeys(List<JtMonkey> monkeys) {
this.monkeys = monkeys;
}
public long getVersion() {
return version;
}
public void setVersion(long version) {
this.version = version;
}
}
@@ -0,0 +1,72 @@
package org.tests.o2m.jointable;
import io.ebean.BaseTestCase;
import io.ebean.Ebean;
import org.ebeantest.LoggedSqlCollector;
import org.junit.Test;
import java.util.Arrays;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
public class TestOneToManyJoinTableNoTableName extends BaseTestCase {
private JtMonkeyGroup troop = new JtMonkeyGroup("Pink");
private JtMonkey m0 = new JtMonkey("Sim3");
private JtMonkey m1 = new JtMonkey("Tim3");
private JtMonkey m2 = new JtMonkey("Uim3");
private void initialInsert() {
Ebean.saveAll(Arrays.asList(troop, m0, m1, m2));
}
@Test
public void base() {
initialInsert();
LoggedSqlCollector.start();
// make m0 dirty ... but no cascade saved?
m0.setFoodPreference("camera");
troop.getMonkeys().add(m0);
troop.getMonkeys().add(m1);
Ebean.save(troop);
List<String> sql = LoggedSqlCollector.current();
assertThat(sql).hasSize(1);
assertThat(sql.get(0)).contains("insert into mkeygroup_monkey (mkeygroup_pid, monkey_mid) values (?, ?)");
int intersectionRows = Ebean.createSqlQuery("select count(*) as total from mkeygroup_monkey where mkeygroup_pid = ?")
.setParameter(1, troop.getPid())
.findOne()
.getInteger("total");
assertThat(intersectionRows).isEqualTo(2);
LoggedSqlCollector.current();
JtMonkeyGroup fetchTroop = Ebean.find(JtMonkeyGroup.class)
.fetch("monkeys")
.where().idEq(troop.getPid())
.findOne();
assertThat(fetchTroop.getMonkeys()).hasSize(2);
sql = LoggedSqlCollector.current();
assertThat(sql).hasSize(1);
assertThat(trimSql(sql.get(0))).contains("from mkeygroup t0 left join mkeygroup_monkey t1z_ on t1z_.mkeygroup_pid = t0.pid left join monkey t1 on t1.mid = t1z_.monkey_mid where t0.pid = ?");
assertThat(trimSql(sql.get(0))).contains("select t0.pid, t0.name, t0.version, t1.mid, t1.name, t1.food_preference, t1.version");
Ebean.delete(troop);
sql = LoggedSqlCollector.stop();
assertThat(sql).hasSize(2);
assertThat(sql.get(0)).contains("delete from mkeygroup_monkey where mkeygroup_pid = ?");
assertThat(sql.get(1)).contains("delete from mkeygroup where pid=? and version=?");
}
}
@@ -0,0 +1,64 @@
package org.tests.query;
import io.ebean.BaseTestCase;
import org.junit.Test;
import org.tests.model.basic.Contact;
import org.tests.model.basic.Customer;
import org.tests.model.basic.ResetBasicData;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
public class TestQueryFindNative extends BaseTestCase {
@Test
public void joinFromManyToOne() {
ResetBasicData.reset();
String sql =
"select c.id, c.first_name, c.last_name, t.id, t.name " +
" from contact c " +
" join o_customer t on t.id = c.customer_id " +
" where t.name like ? " +
" order by c.first_name, c.last_name";
List<Contact> contacts =
server()
.findNative(Contact.class, sql)
.setParameter(1, "Rob")
.findList();
assertThat(contacts).isNotEmpty();
Customer customer = contacts.get(0).getCustomer();
assertThat(customer).isNotNull();
}
@Test
public void joinFromOneToMany() {
ResetBasicData.reset();
String sql =
"select cu.id, cu.name, ct.id, ct.first_name " +
" from o_customer cu " +
" left join contact ct on cu.id = ct.customer_id " +
" where cu.name like ? " +
" order by name";
List<Customer> customers =
server()
.findNative(Customer.class, sql)
.setParameter(1, "Rob")
.findList();
assertThat(customers).isNotEmpty();
List<Contact> contacts = customers.get(0).getContacts();
assertThat(contacts).isNotEmpty();
}
}
@@ -15,15 +15,21 @@
<alterColumn columnName="one_id" tableName="migtest_fk_set_null" references="migtest_fk_one.id" foreignKeyName="fk_migtest_fk_set_null_one_id" foreignKeyIndex="ix_migtest_fk_set_null_one_id" foreignKeyOnDelete="RESTRICT" foreignKeyOnUpdate="RESTRICT" dropForeignKey="fk_migtest_fk_set_null_one_id" dropForeignKeyIndex="ix_migtest_fk_set_null_one_id"/>
<alterColumn columnName="status" tableName="migtest_e_basic" currentType="varchar(1)" defaultValue="'A'" notnull="true" currentNotnull="false" checkConstraint="check ( status in ('N','A','I','?'))" checkConstraintName="ck_migtest_e_basic_status"/>
<alterColumn columnName="description" tableName="migtest_e_basic" unique="uq_migtest_e_basic_description">
<before>-- rename all collisions</before>
<before>
<ddl>-- rename all collisions</ddl>
</before>
</alterColumn>
<alterColumn columnName="user_id" tableName="migtest_e_basic" currentType="integer" notnull="false" currentNotnull="true" references="migtest_e_user.id" foreignKeyName="fk_migtest_e_basic_user_id" foreignKeyIndex="ix_migtest_e_basic_user_id">
<before>insert into migtest_e_user (id) select distinct user_id from migtest_e_basic</before>
<before>
<ddl>insert into migtest_e_user (id) select distinct user_id from migtest_e_basic</ddl>
</before>
</alterColumn>
<addColumn tableName="migtest_e_basic">
<column name="new_string_field" type="varchar" defaultValue="'foo''bar'" notnull="true"/>
<column name="new_boolean_field" type="boolean" defaultValue="true" notnull="true">
<after>update ${table} set ${column} = old_boolean</after>
<after>
<ddl>update ${table} set ${column} = old_boolean</ddl>
</after>
</column>
<column name="new_boolean_field2" type="boolean" defaultValue="true" notnull="true"/>
<column name="progress" type="integer" defaultValue="0" notnull="true" checkConstraint="check ( progress in (0,1,2))" checkConstraintName="ck_migtest_e_basic_progress"/>
@@ -31,14 +37,16 @@
</addColumn>
<addUniqueConstraint constraintName="uq_migtest_e_basic_indextest2" tableName="migtest_e_basic" columnNames="DROP CONSTRAINT" nullableColumns="indextest2"/>
<addUniqueConstraint constraintName="uq_migtest_e_basic_indextest6" tableName="migtest_e_basic" columnNames="DROP CONSTRAINT" nullableColumns="indextest6"/>
<addUniqueConstraint constraintName="uq_migtest_e_basic_status_indextest1" tableName="migtest_e_basic" columnNames="status,indextest1" nullableColumns="indextest1" oneToOne="false"/>
<addUniqueConstraint constraintName="uq_migtest_e_basic_name" tableName="migtest_e_basic" columnNames="name" nullableColumns="name" oneToOne="false"/>
<addUniqueConstraint constraintName="uq_migtest_e_basic_indextest4" tableName="migtest_e_basic" columnNames="indextest4" nullableColumns="indextest4" oneToOne="false"/>
<addUniqueConstraint constraintName="uq_migtest_e_basic_indextest5" tableName="migtest_e_basic" columnNames="indextest5" nullableColumns="indextest5" oneToOne="false"/>
<addUniqueConstraint constraintName="uq_migtest_e_basic_status_indextest1" tableName="migtest_e_basic" columnNames="status,indextest1" oneToOne="false" nullableColumns="indextest1"/>
<addUniqueConstraint constraintName="uq_migtest_e_basic_name" tableName="migtest_e_basic" columnNames="name" oneToOne="false" nullableColumns="name"/>
<addUniqueConstraint constraintName="uq_migtest_e_basic_indextest4" tableName="migtest_e_basic" columnNames="indextest4" oneToOne="false" nullableColumns="indextest4"/>
<addUniqueConstraint constraintName="uq_migtest_e_basic_indextest5" tableName="migtest_e_basic" columnNames="indextest5" oneToOne="false" nullableColumns="indextest5"/>
<alterColumn columnName="test_status" tableName="migtest_e_enum" dropCheckConstraint="ck_migtest_e_enum_test_status"/>
<addHistoryTable baseTable="migtest_e_history"/>
<alterColumn columnName="test_string" tableName="migtest_e_history" type="bigint" currentType="varchar" currentNotnull="false" comment="Column altered to long now">
<before platforms="postgres">alter table ${table} alter column ${column} TYPE bigint USING (${column}::integer)</before>
<before platforms="postgres">
<ddl>alter table ${table} alter column ${column} TYPE bigint USING (${column}::integer)</ddl>
</before>
</alterColumn>
<addTableComment name="migtest_e_history" comment="We have history now"/>
<alterColumn columnName="test_string" tableName="migtest_e_history2" withHistory="true" currentType="varchar" defaultValue="'unknown'" notnull="true" currentNotnull="false"/>
@@ -18,8 +18,8 @@
<addUniqueConstraint constraintName="uq_migtest_e_basic_name" tableName="migtest_e_basic" columnNames="DROP CONSTRAINT" nullableColumns="name"/>
<addUniqueConstraint constraintName="uq_migtest_e_basic_indextest4" tableName="migtest_e_basic" columnNames="DROP CONSTRAINT" nullableColumns="indextest4"/>
<addUniqueConstraint constraintName="uq_migtest_e_basic_indextest5" tableName="migtest_e_basic" columnNames="DROP CONSTRAINT" nullableColumns="indextest5"/>
<addUniqueConstraint constraintName="uq_migtest_e_basic_indextest2" tableName="migtest_e_basic" columnNames="indextest2" nullableColumns="indextest2" oneToOne="false"/>
<addUniqueConstraint constraintName="uq_migtest_e_basic_indextest6" tableName="migtest_e_basic" columnNames="indextest6" nullableColumns="indextest6" oneToOne="false"/>
<addUniqueConstraint constraintName="uq_migtest_e_basic_indextest2" tableName="migtest_e_basic" columnNames="indextest2" oneToOne="false" nullableColumns="indextest2"/>
<addUniqueConstraint constraintName="uq_migtest_e_basic_indextest6" tableName="migtest_e_basic" columnNames="indextest6" oneToOne="false" nullableColumns="indextest6"/>
<alterColumn columnName="test_status" tableName="migtest_e_enum" checkConstraint="check ( test_status in ('N','A','I'))" checkConstraintName="ck_migtest_e_enum_test_status"/>
<alterColumn columnName="test_string" tableName="migtest_e_history" withHistory="true" comment="DROP COMMENT"/>
<addTableComment name="migtest_e_history" comment="DROP COMMENT"/>