Compare commits

...
Author SHA1 Message Date
Rob Bygrave ef8d9c7eb0 [maven-release-plugin] prepare release ebean-parent-12.16.0 2022-03-16 22:49:16 +13:00
Rob Bygrave a464c763d0 Bump parent pom 2022-03-16 22:45:57 +13:00
Rob Bygrave d4d1e571d3 Fix test assert for multi-database 2022-03-16 22:37:54 +13:00
Rob Bygrave 92e85e32a2 #2598 - H2 + @Lob String fails with - Error[Value too long for column "CHARACTER VARYING": "... (1048577)" [22001-210]] 2022-03-16 22:13:52 +13:00
Rob Bygrave ff1b01e5ca Bump agent to 12.16.0 + test and provided dependencies 2022-03-16 22:13:07 +13:00
Rob Bygrave 327f25c568 No functional change - refactor tidy dealing with some internal warnings
protected not needed on final classes
2022-03-16 18:51:12 +13:00
Rob BygraveandGitHub 00a1324c52 Merge pull request #2596 from FOCONIS/ddl-refactor8
DDl-Refactor: Add DB2 Reorg support and history support
2022-03-16 18:47:58 +13:00
Rob Bygrave 75b9df942c No effective change - tidy TestWithCacheAndLazyLoad 2022-03-16 18:38:47 +13:00
Rob Bygrave 946b390037 Merge branch 'FOCONIS-lazyload-npe' 2022-03-16 18:37:48 +13:00
Rob Bygrave 3a1e1e23f5 Merge branch 'lazyload-npe' of https://github.com/FOCONIS/ebean into FOCONIS-lazyload-npe 2022-03-16 18:14:22 +13:00
Rob Bygrave d9ea465102 Support running tests against java 19 2022-03-16 18:02:07 +13:00
Rob Bygrave 3bc7f061eb Bump test dependencies for junit, assertj, mockito 2022-03-16 17:55:37 +13:00
Rob Bygrave 0bc07d38b2 Add Oracle GH workflow for JDK EA 18,19,loom,valhalla,metropolis,panama 2022-03-16 12:13:29 +13:00
Roland Praml 79f6d6d0f0 Ignored query plans for DB2 2022-03-15 17:18:34 +01:00
Roland Praml 782e41098d enabled history tests 2022-03-15 16:56:00 +01:00
Roland Praml 0225fddf6d FIX: AlterColumnTest 2022-03-15 16:31:43 +01:00
Roland Praml be3e08018e Reference Scripts 2022-03-15 15:59:50 +01:00
Roland Praml c9204eed3f DB2: HistorySupport added 2022-03-15 15:59:33 +01:00
Noemi Szemenyei edb6a0ebe2 performance boost list --> set
(cherry picked from commit 2478aded70)
2022-03-15 13:21:10 +01:00
Noemi Szemenyei 826b507b77 Suggested fix
(cherry picked from commit 52ca271a13)
2022-03-15 13:21:02 +01:00
Noemi Szemenyei 8ad8f47c6b Created failing test case
(cherry picked from commit 2b21588c90)
2022-03-15 13:20:16 +01:00
Rob BygraveandGitHub 5944777a13 Merge pull request #2594 from FOCONIS/ddl-refactor7
[DDL-Refactor] History table generation
2022-03-15 09:23:30 +13:00
Roland Praml 05a1b0d64e DB2: Migration is working now 2022-03-14 16:31:34 +01:00
Roland Praml 4d62958645 Updated DDL scripts to support notnull -> null transitions 2022-03-14 14:13:56 +01:00
Roland Praml 610a8b2df6 FIX: notNull -> null transition must be applied also to history 2022-03-14 14:13:10 +01:00
Roland Praml 5b8bb41598 Postgres: Drops history support during alter and restores it after that 2022-03-14 14:08:42 +01:00
Roland Praml 9e16d955ae Sqlserver: no effective code change 2022-03-14 14:06:25 +01:00
Roland Praml 197bb0509a Nuodb: no effective ddl change 2022-03-14 14:05:42 +01:00
Roland Praml 5536aeb829 Mysql: locks only one table at once 2022-03-14 14:04:59 +01:00
Roland Praml d629fb2dbd MariaDB uses '@@system_versioning_alter_history = 1' now. Removed obsolete "lock tables" statements 2022-03-14 14:01:21 +01:00
Roland Praml 10c01574d2 H2: No effective change Statements reordered 2022-03-14 13:12:24 +01:00
Roland Praml cfad20c8e1 Refactored Hisotry generation 2022-03-14 13:11:00 +01:00
Rob Bygrave 0ea375b4fc Merge branch 'FOCONIS-ddl-clickhouse' 2022-03-14 16:54:07 +13:00
Rob Bygrave 71fce1b4c5 #2575 Tidy up for [DDL-ClickHouse] fixes for ClickHouse platform 2022-03-14 16:53:51 +13:00
Rob Bygrave db60065389 Merge branch 'ddl-clickhouse' of https://github.com/FOCONIS/ebean into FOCONIS-ddl-clickhouse 2022-03-14 16:51:27 +13:00
Rob Bygrave d3a765a532 Tidy whitespace only, no effective change 2022-03-14 16:19:02 +13:00
Rob Bygrave fd4be0ca59 #2593 - Tidy SqlTreeBuilder to not add duplicate BeanProperties 2022-03-14 16:16:14 +13:00
Rob Bygrave 267a81b792 #25890 - Tidy only, no effective change for BackgroundExecutorWrapper 2022-03-14 15:46:21 +13:00
Rob Bygrave 1615beadd1 Merge branch 'master' of github.com:ebean-orm/ebean 2022-03-14 15:37:52 +13:00
Rob Bygrave a3e5c3de72 Merge branch 'FOCONIS-Bug-wrong-join-inheritance-dbforeignkey' 2022-03-14 15:37:38 +13:00
Rob Bygrave 267b1b7303 #2580 - Fix for Wrong join in query with @Inheritance
Effectively delays an extra join added via BeanPropertyAssocOne with inheritance, such that this only occurs if a SqlTreeNode child doesn't add the join.
2022-03-14 15:37:12 +13:00
Rob Bygrave c902caed24 Fix is to delay the extra join added via BeanPropertyAssocOne until after the children nodes have joined.
In this way the join is ONLY added if not already done so via a child node (SqlTreeNode).
2022-03-14 15:32:42 +13:00
Rob BygraveandGitHub 662666e1a4 Merge pull request #2590 from FOCONIS/background-executor-wrapper
Provide custom BackgroundExecutorWrapper to pass thread locals
2022-03-14 10:40:37 +13:00
Rob BygraveandGitHub 394f245e33 Merge pull request #2591 from FOCONIS/ddl-refactor6
[DDL-refactor] Sorted the statements, so that they are going to the correct buffers
2022-03-14 09:17:17 +13:00
Roland Praml 89da952b15 updated scripts 2022-03-11 15:35:15 +01:00
Roland Praml d927b8c2c2 [DDL-refactor] Sorted the statements, so that they are going to the correct buffers 2022-03-11 15:34:37 +01:00
Noemi Szemenyei 6867c4568c fix db.shutdown()
(cherry picked from commit 33d17d0f49)
2022-03-11 13:32:08 +01:00
Noemi Szemenyei e0e572a8b4 update
(cherry picked from commit dab8ed8a3b)
2022-03-11 13:31:58 +01:00
Noemi Szemenyei 3c7555de36 Provide custom BackgroundExecutorWrapper to pass thread locals
(cherry picked from commit 336accf447)
2022-03-11 13:31:50 +01:00
Rob BygraveandGitHub 75662aac5c Merge pull request #2589 from FOCONIS/ddl-refactor5
No effective DDL change - refactor whitespace and add buffer comments
2022-03-11 10:38:14 +13:00
Roland Praml e4ddcbd40a Reference scripts 2022-03-10 13:39:59 +01:00
Roland Praml a8f390c3b7 No effective DDL change - removed obsolete whitespaces 2022-03-10 13:31:12 +01:00
Roland Praml f1b196cf2e FIX: tests and pushed reference ddl scripts 2022-03-10 13:24:39 +01:00
Roland Praml 04675ea76b No effective DDL change, just added buffer comments in DDL 2022-03-10 13:24:01 +01:00
Rob Bygrave 8e44ad71e8 Merge branch 'Bug-wrong-join-inheritance-dbforeignkey' of https://github.com/FOCONIS/ebean into FOCONIS-Bug-wrong-join-inheritance-dbforeignkey 2022-03-10 16:24:14 +13:00
Rob Bygrave febd56c9c0 #2581 - SoftDelete on @OneToMany is ignored after refresh() (since 12.11.5) 2022-03-10 16:19:11 +13:00
Rob Bygrave 0d3649853a Bump provided optional Jackson dependency to 2.13.2 2022-03-10 15:33:33 +13:00
Rob Bygrave beed67567a #2588 - [postgis-jdbc] Bump postgis-jdbc dependency from 2.2.1 to 2.5.1 2022-03-10 15:33:00 +13:00
Rob BygraveandGitHub 0fe0803374 Merge pull request #2587 from FOCONIS/ddl-refactor4
[Ddl refactor] Alter Table changes are grouped by table name.
2022-03-10 13:00:44 +13:00
Roland Praml 5fd7aca3d9 Fixed test cases 2022-03-09 16:17:04 +01:00
Rob BygraveandGitHub 348fecddf7 Merge pull request #2586 from FOCONIS/ddl-refactor3
[Clickhouse] Refactored columnNotNull
2022-03-09 22:10:59 +13:00
Rob BygraveandGitHub b333449d84 Merge pull request #2584 from FOCONIS/hazelcast-unmodifiable-map
Small code change for Hazelcast 4.1.5
2022-03-09 16:10:39 +13:00
Rob BygraveandGitHub 87a97c2889 Merge pull request #2585 from FOCONIS/ddl-refactor2
Fix DDL Default handling & Prepared PlatformDdl to to have only one 'alterColumn' method
2022-03-09 16:07:30 +13:00
Roland Praml 790147e42d Reviewed DDL changes - all changes are plausible 2022-03-08 17:28:39 +01:00
Roland Praml 3a93abaa7f Only moves in DDL (checked with git -c color.diff.newMoved=black -c color.diff.oldMoved=black diff --color-moved=plain '*.sql') 2022-03-08 12:43:54 +01:00
Roland Praml 1049d951d9 Code changes to group alterTables by tableName 2022-03-08 12:39:44 +01:00
Roland Praml c3bdac5271 [Clickhouse] Refactored columnNotNull 2022-03-07 16:41:27 +01:00
Roland Praml 9bcba0b80d Sql-scripts 2022-03-07 15:40:45 +01:00
Roland Praml 1529a2f12b Prepared PlatformDdl to to have only one 'alterColumn' method (required for further refactoring) 2022-03-07 15:35:36 +01:00
Noemi Szemenyei b41b6da40e iterator.remove() is not needed 2022-03-04 08:20:44 +01:00
Rob BygraveandGitHub 4285cbaeb2 Merge pull request #2583 from ebean-orm/feature/2582-bg-executor
#2582 - Fix for regression, BackgroundExecutor is bounded (since 12.6.2)
2022-03-04 11:15:23 +13:00
Rob BygraveandGitHub 26fd7c5498 Merge pull request #2578 from ebean-orm/feature/bump-ddl-runner
Bump to ebean-ddl-runner 1.3
2022-03-04 11:14:35 +13:00
Rob BygraveandGitHub 6ec4e4b965 Merge branch 'master' into feature/bump-ddl-runner 2022-03-04 10:26:17 +13:00
Rob Bygrave e3a20a351d #2582 - Fix for regression, BackgroundExecutor is bounded (since 12.6.2)
The change for #2121 brought in a regression where the scheduledExecutorService was used for processing ALL submitted tasks (not just scheduled ones) and is a bounded executor service.  Previously non-scheduled tasks went to a "newCachedThreadPool" based executor service and with this change we are moving back to that (via restoring and using the DaemonExecutorService for those tasks).
2022-03-04 10:21:56 +13:00
Alexander Wagner 9b8f7be98f Bug: Wrong join in query with @Inheritance and @DbForeignKey
(cherry picked from commit 4bcadbb4da3f0defbcc58802e621442d948df38a)
2022-03-02 11:50:57 +01:00
Rob Bygrave 61f5442e3b #2579 - Bump to ebean-migration 12.16.0, support both basePlatform and platform for finding/running migrations
Fix ebean-migration-auto.version to 1.2
2022-03-02 12:30:32 +13:00
Rob Bygrave c5acd31da4 #2579 - Bump to ebean-migration 12.16.0, support both basePlatform and platform for finding/running migrations 2022-03-02 12:25:57 +13:00
Rob Bygrave 49fde7654e Bump to 12.16.0-SNAPSHOT 2022-03-02 12:17:38 +13:00
Rob Bygrave 05338531ed Bump versions after release 2022-03-01 23:01:09 +13:00
Rob Bygrave eb4d906814 [maven-release-plugin] prepare for next development iteration 2022-03-01 22:47:01 +13:00
Rob Bygrave f571da5154 Bump to ebean-ddl-runner 1.3 2022-03-01 22:35:40 +13:00
Roland Praml b88e4a35f8 Ddl fixes for clickhouse platform 2022-02-25 12:10:54 +01:00
262 changed files with 8571 additions and 5834 deletions
+39
View File
@@ -0,0 +1,39 @@
name: JDK EA
on:
workflow_dispatch:
schedule:
- cron: '39 6 * * 1,3,5'
jobs:
build:
runs-on: ${{ matrix.os }}
permissions:
contents: read
packages: write
strategy:
fail-fast: false
matrix:
java_version: [17,18,19,loom,valhalla,metropolis,panama]
os: [ubuntu-latest]
steps:
- uses: actions/checkout@v2
- name: Set up Java
uses: oracle-actions/setup-java@v1
with:
website: jdk.java.net
release: ${{ matrix.java_version }}
- name: Maven cache
uses: actions/cache@v2
env:
cache-name: maven-cache
with:
path:
~/.m2
key: build-${{ env.cache-name }}
- name: Build with Maven
run: mvn package
+2 -2
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>12.15.1</version>
<version>12.16.0</version>
</parent>
<name>ebean api</name>
@@ -106,7 +106,7 @@
<dependency>
<groupId>io.avaje</groupId>
<artifactId>junit</artifactId>
<version>1.0</version>
<version>1.1</version>
<scope>test</scope>
</dependency>
@@ -829,7 +829,7 @@ public final class EntityBeanIntercept implements Serializable {
/**
* Load the bean when it is a reference.
*/
protected void loadBean(int loadProperty) {
void loadBean(int loadProperty) {
lock.lock();
try {
if (beanLoader == null) {
@@ -886,7 +886,7 @@ public final class EntityBeanIntercept implements Serializable {
* Helper method to check if two objects are equal.
*/
@SuppressWarnings({"unchecked", "rawtypes"})
protected static boolean notEqual(Object obj1, Object obj2) {
static boolean notEqual(Object obj1, Object obj2) {
if (obj1 == null) {
return (obj2 != null);
}
@@ -0,0 +1,42 @@
package io.ebean.config;
import java.util.concurrent.Callable;
/**
* BackgroundExecutorWrapper that can be used to wrap tasks that are sent to background (i.e. another thread).
* It should copy all necessary thread-local variables. See {@link MdcBackgroundExecutorWrapper} for implementation details.
*
* @author Roland Praml, FOCONIS AG
*/
public interface BackgroundExecutorWrapper {
/**
* Wrap the task with MDC context if defined.
*/
<T> Callable<T> wrap(Callable<T> task);
/**
* Wrap the task with MDC context if defined.
*/
Runnable wrap(Runnable task);
/**
* Combines two wrappers by nesting them.
*/
default BackgroundExecutorWrapper with(BackgroundExecutorWrapper inner) {
return new BackgroundExecutorWrapper() {
@Override
public Runnable wrap(Runnable task) {
return BackgroundExecutorWrapper.this.wrap(inner.wrap(task));
}
@Override
public <T> Callable<T> wrap(Callable<T> task) {
return BackgroundExecutorWrapper.this.wrap(inner.wrap(task));
}
};
}
}
@@ -445,6 +445,7 @@ public class DatabaseConfig {
private int backgroundExecutorSchedulePoolSize = 1;
private int backgroundExecutorShutdownSecs = 30;
private BackgroundExecutorWrapper backgroundExecutorWrapper = new MdcBackgroundExecutorWrapper();
// defaults for the L2 bean caching
@@ -1463,6 +1464,20 @@ public class DatabaseConfig {
this.backgroundExecutorShutdownSecs = backgroundExecutorShutdownSecs;
}
/**
* Return the background executor wrapper.
*/
public BackgroundExecutorWrapper getBackgroundExecutorWrapper() {
return backgroundExecutorWrapper;
}
/**
* Sets the background executor wrapper. The wrapper is used when a task is sent to background and should copy the thread-locals.
*/
public void setBackgroundExecutorWrapper(BackgroundExecutorWrapper backgroundExecutorWrapper) {
this.backgroundExecutorWrapper = backgroundExecutorWrapper;
}
/**
* Return the L2 cache default max size.
*/
@@ -2884,6 +2899,7 @@ public class DatabaseConfig {
backgroundExecutorSchedulePoolSize = p.getInt("backgroundExecutorSchedulePoolSize", backgroundExecutorSchedulePoolSize);
backgroundExecutorShutdownSecs = p.getInt("backgroundExecutorShutdownSecs", backgroundExecutorShutdownSecs);
backgroundExecutorWrapper = p.createInstance(BackgroundExecutorWrapper.class, "backgroundExecutorWrapper", backgroundExecutorWrapper);
disableClasspathSearch = p.getBoolean("disableClasspathSearch", disableClasspathSearch);
currentUserProvider = p.createInstance(CurrentUserProvider.class, "currentUserProvider", currentUserProvider);
databasePlatform = p.createInstance(DatabasePlatform.class, "databasePlatform", databasePlatform);
@@ -0,0 +1,52 @@
package io.ebean.config;
import org.slf4j.MDC;
import java.util.Map;
import java.util.concurrent.Callable;
/**
* Propagates MDC context for tasks executed in the background.
*/
public final class MdcBackgroundExecutorWrapper implements BackgroundExecutorWrapper {
/**
* Wrap the task with MDC context if defined.
*/
@Override
public <T> Callable<T> wrap(Callable<T> task) {
final Map<String, String> map = MDC.getCopyOfContextMap();
if (map == null) {
return task;
} else {
return () -> {
MDC.setContextMap(map);
try {
return task.call();
} finally {
MDC.clear();
}
};
}
}
/**
* Wrap the task with MDC context if defined.
*/
@Override
public Runnable wrap(Runnable task) {
final Map<String, String> map = MDC.getCopyOfContextMap();
if (map == null) {
return task;
} else {
return () -> {
MDC.setContextMap(map);
try {
task.run();
} finally {
MDC.clear();
}
};
}
}
}
@@ -18,6 +18,8 @@ public class ClickHousePlatform extends DatabasePlatform {
//this.exceptionTranslator =
this.nativeUuidType = true;
this.dbDefaultValue.setNow("now()");
this.dbDefaultValue.setFalse("0");
this.dbDefaultValue.setTrue("1");
this.dbIdentity.setIdType(IdType.IDENTITY);
this.dbIdentity.setSupportsGetGeneratedKeys(false);
@@ -36,6 +36,7 @@ public abstract class BaseDB2Platform extends DatabasePlatform {
.addDataIntegrity("23502","23503","23504","23511","23512","23511","42917","23515")
.build();
historySupport = new DB2HistorySupport();
booleanDbType = Types.BOOLEAN;
dbTypeMap.put(DbType.TINYINT, new DbPlatformType("smallint", false));
dbTypeMap.put(DbType.INTEGER, new DbPlatformType("integer", false));
@@ -0,0 +1,30 @@
package io.ebean.config.dbplatform.db2;
import io.ebean.config.dbplatform.DbStandardHistorySupport;
/**
* DB2 based history support.
*/
public class DB2HistorySupport extends DbStandardHistorySupport {
@Override
public String getAsOfViewSuffix(String asOfViewSuffix) {
return " for system_time as of ?";
}
@Override
public String getVersionsBetweenSuffix(String asOfViewSuffix) {
return " for system_time between ? and ?";
}
@Override
public String getSysPeriodLower(String tableAlias, String sysPeriod) {
return tableAlias + "." + sysPeriod + "_start";
}
@Override
public String getSysPeriodUpper(String tableAlias, String sysPeriod) {
return tableAlias + "." + sysPeriod + "_end";
}
}
+4 -4
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>12.15.1</version>
<version>12.16.0</version>
</parent>
<!-- <parent>-->
<!-- <groupId>org.avaje</groupId>-->
@@ -14,7 +14,7 @@
<scm>
<developerConnection>scm:git:git@github.com:ebean-orm/ebean.git</developerConnection>
<tag>ebean-parent-12.15.1</tag>
<tag>ebean-parent-12.16.0</tag>
</scm>
<name>ebean autotune</name>
@@ -26,7 +26,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>12.15.1</version>
<version>12.16.0</version>
<scope>provided</scope>
</dependency>
@@ -48,7 +48,7 @@
<dependency>
<groupId>io.avaje</groupId>
<artifactId>junit</artifactId>
<version>1.0</version>
<version>1.1</version>
<scope>test</scope>
</dependency>
+15 -15
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>12.15.1</version>
<version>12.16.0</version>
</parent>
<name>ebean bom</name>
@@ -71,88 +71,88 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean</artifactId>
<version>12.15.1</version>
<version>12.16.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>12.15.1</version>
<version>12.16.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>12.15.1</version>
<version>12.16.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core-type</artifactId>
<version>12.15.1</version>
<version>12.16.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-ddl-generator</artifactId>
<version>12.15.1</version>
<version>12.16.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-externalmapping-api</artifactId>
<version>12.15.1</version>
<version>12.16.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-externalmapping-xml</artifactId>
<version>12.15.1</version>
<version>12.16.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-autotune</artifactId>
<version>12.15.1</version>
<version>12.16.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>12.15.1</version>
<version>12.16.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>querybean-generator</artifactId>
<version>12.15.1</version>
<version>12.16.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>kotlin-querybean-generator</artifactId>
<version>12.15.1</version>
<version>12.16.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-test</artifactId>
<version>12.15.1</version>
<version>12.16.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-postgis</artifactId>
<version>12.15.1</version>
<version>12.16.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-redis</artifactId>
<version>12.15.1</version>
<version>12.16.0</version>
</dependency>
</dependencies>
+2 -2
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>12.15.1</version>
<version>12.16.0</version>
</parent>
<artifactId>ebean-core-type</artifactId>
@@ -16,7 +16,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>12.15.1</version>
<version>12.16.0</version>
</dependency>
<dependency>
+8 -8
View File
@@ -3,7 +3,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>12.15.1</version>
<version>12.16.0</version>
</parent>
<artifactId>ebean-core</artifactId>
@@ -15,7 +15,7 @@
<scm>
<developerConnection>scm:git:git@github.com:ebean-orm/ebean.git</developerConnection>
<tag>ebean-parent-12.15.1</tag>
<tag>ebean-parent-12.16.0</tag>
</scm>
<dependencies>
@@ -23,7 +23,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-ddl-runner</artifactId>
<version>1.2</version>
<version>${ebean-ddl-runner.version}</version>
</dependency>
<dependency>
@@ -35,25 +35,25 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-migration-auto</artifactId>
<version>1.1</version>
<version>${ebean-migration-auto.version}</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>12.15.1</version>
<version>12.16.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core-type</artifactId>
<version>12.15.1</version>
<version>12.16.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-externalmapping-api</artifactId>
<version>12.15.1</version>
<version>12.16.0</version>
</dependency>
<dependency>
@@ -152,7 +152,7 @@
<dependency>
<groupId>io.avaje</groupId>
<artifactId>junit</artifactId>
<version>1.0</version>
<version>1.1</version>
<scope>test</scope>
</dependency>
@@ -4,7 +4,7 @@ import io.ebean.bean.EntityBeanIntercept;
import io.ebean.bean.PersistenceContext;
import io.ebeaninternal.server.deploy.BeanDescriptor;
import java.util.List;
import java.util.Set;
/**
* A buffer of beans for batch lazy loading and secondary query loading.
@@ -13,7 +13,7 @@ public interface LoadBeanBuffer {
int batchSize();
List<EntityBeanIntercept> batch();
Set<EntityBeanIntercept> batch();
BeanDescriptor<?> descriptor();
@@ -16,7 +16,7 @@ import java.util.Set;
*/
public final class LoadBeanRequest extends LoadRequest {
private final List<EntityBeanIntercept> batch;
private final Set<EntityBeanIntercept> batch;
private final LoadBeanBuffer loadBuffer;
private final String lazyLoadProperty;
private final boolean loadCache;
@@ -58,7 +58,7 @@ public final class LoadBeanRequest extends LoadRequest {
/**
* Return the batch of beans to actually load.
*/
public List<EntityBeanIntercept> batch() {
public Set<EntityBeanIntercept> batch() {
return batch;
}
@@ -106,7 +106,9 @@ final class DScriptRunner implements ScriptRunner {
try (Connection connection = obtainConnection()) {
DdlRunner runner = new DdlRunner(useAutoCommit, scriptName, platformName);
runner.runAll(content, connection);
connection.commit();
if (!connection.getAutoCommit()) {
connection.commit();
}
runner.runNonTransactional(connection);
}
@@ -2,7 +2,6 @@ package io.ebeaninternal.server.core;
import io.ebean.CacheMode;
import io.ebean.ExpressionList;
import io.ebean.Transaction;
import io.ebean.bean.BeanCollection;
import io.ebean.bean.EntityBean;
import io.ebean.bean.EntityBeanIntercept;
@@ -17,6 +16,7 @@ import org.slf4j.Logger;
import javax.persistence.EntityNotFoundException;
import java.util.List;
import java.util.Set;
/**
* Helper to handle lazy loading and refreshing of beans.
@@ -39,14 +39,14 @@ final class DefaultBeanLoader {
}
void loadMany(BeanCollection<?> bc, boolean onlyIds) {
loadManyInternal(bc.getOwnerBean(), bc.getPropertyName(), null, false, onlyIds);
loadManyInternal(bc.getOwnerBean(), bc.getPropertyName(), false, onlyIds);
}
void refreshMany(EntityBean parentBean, String propertyName) {
loadManyInternal(parentBean, propertyName, null, true, false);
loadManyInternal(parentBean, propertyName, true, false);
}
private void loadManyInternal(EntityBean parentBean, String propertyName, Transaction t, boolean refresh, boolean onlyIds) {
private void loadManyInternal(EntityBean parentBean, String propertyName, boolean refresh, boolean onlyIds) {
EntityBeanIntercept ebi = parentBean._ebean_getIntercept();
PersistenceContext pc = ebi.getPersistenceContext();
BeanDescriptor<?> parentDesc = server.descriptor(parentBean.getClass());
@@ -105,7 +105,7 @@ final class DefaultBeanLoader {
query.setReadOnly(true);
}
server.findOne(query, t);
server.findOne(query, null);
if (beanCollection != null) {
if (beanCollection.checkEmptyLazyLoad()) {
if (log.isDebugEnabled()) {
@@ -121,7 +121,7 @@ final class DefaultBeanLoader {
* Load a batch of beans for +query or +lazy loading.
*/
void loadBean(LoadBeanRequest loadRequest) {
List<EntityBeanIntercept> batch = loadRequest.batch();
Set<EntityBeanIntercept> batch = loadRequest.batch();
if (batch.isEmpty()) {
throw new RuntimeException("Nothing in batch?");
}
@@ -199,7 +199,7 @@ final class DefaultBeanLoader {
query.setLazyLoadProperty(ebi.getLazyLoadProperty());
if (draft) {
query.asDraft();
} else {
} else if (mode == SpiQuery.Mode.LAZYLOAD_BEAN) {
query.setIncludeSoftDeletes();
}
if (embeddedOwnerIndex > -1) {
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.core;
import io.ebean.config.BackgroundExecutorWrapper;
import io.ebean.config.ContainerConfig;
import io.ebean.config.DatabaseConfig;
import io.ebean.config.DatabaseConfigProvider;
@@ -67,7 +68,8 @@ public final class DefaultContainer implements SpiContainer {
String namePrefix = "ebean-" + config.getName();
int schedulePoolSize = config.getBackgroundExecutorSchedulePoolSize();
int shutdownSecs = config.getBackgroundExecutorShutdownSecs();
return new DefaultBackgroundExecutor(schedulePoolSize, shutdownSecs, namePrefix);
BackgroundExecutorWrapper wrapper = config.getBackgroundExecutorWrapper();
return new DefaultBackgroundExecutor(schedulePoolSize, shutdownSecs, namePrefix, wrapper);
}
/**
@@ -373,7 +373,9 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
migrationRunner.setDefaultDbSchema(dbSchema);
}
migrationRunner.setName(config.getName());
migrationRunner.setPlatform(config.getDatabasePlatform().getPlatform().base().name().toLowerCase());
Platform platform = config.getDatabasePlatform().getPlatform();
migrationRunner.setBasePlatform(platform.base().name().toLowerCase());
migrationRunner.setPlatform(platform.name().toLowerCase());
migrationRunner.loadProperties(config.getProperties());
migrationRunner.run(config.getDataSource());
}
@@ -3,6 +3,7 @@ package io.ebeaninternal.server.deploy;
import io.ebean.bean.EntityBean;
import io.ebean.bean.PersistenceContext;
import io.ebeaninternal.server.query.SqlJoinType;
import io.ebeaninternal.server.query.SqlTreeJoin;
import java.sql.SQLException;
@@ -60,11 +61,28 @@ final class AssocOneHelpRefInherit extends AssocOneHelp {
return ref;
}
@Override
void appendFrom(DbSqlContext ctx, SqlJoinType joinType) {
// add join to support the discriminator column
String relativePrefix = ctx.getRelativePrefix(property.name);
property.tableJoin.addJoin(joinType, relativePrefix, ctx);
ctx.addExtraJoin(new Extra(relativePrefix, joinType));
}
/**
* Extra join to support the discriminator column.
*/
final class Extra implements SqlTreeJoin {
final String relativePrefix;
final SqlJoinType joinType;
Extra(String relativePrefix, SqlJoinType joinType) {
this.relativePrefix = relativePrefix;
this.joinType = joinType;
}
@Override
public void addJoin(DbSqlContext ctx) {
// add join to support the discriminator column *IF* join is not already present
property.tableJoin.addJoin(joinType, relativePrefix, ctx);
}
}
/**
@@ -1310,8 +1310,8 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
* Hit the bean cache trying to load a list/batch of entities.
* Return the set of entities that were successfully loaded from L2 cache.
*/
public Set<EntityBeanIntercept> cacheBeanLoadAll(List<EntityBeanIntercept> list, PersistenceContext persistenceContext, int lazyLoadProperty, String propertyName) {
return cacheHelp.beanCacheLoadAll(list, persistenceContext, lazyLoadProperty, propertyName);
public Set<EntityBeanIntercept> cacheBeanLoadAll(Set<EntityBeanIntercept> batch, PersistenceContext persistenceContext, int lazyLoadProperty, String propertyName) {
return cacheHelp.beanCacheLoadAll(batch, persistenceContext, lazyLoadProperty, propertyName);
}
/**
@@ -693,9 +693,9 @@ final class BeanDescriptorCacheHelp<T> {
/**
* Load a batch of entities from L2 bean cache checking the lazy loaded property is loaded.
*/
Set<EntityBeanIntercept> beanCacheLoadAll(List<EntityBeanIntercept> list, PersistenceContext context, int lazyLoadProperty, String propertyName) {
Set<EntityBeanIntercept> beanCacheLoadAll(Set<EntityBeanIntercept> batch, PersistenceContext context, int lazyLoadProperty, String propertyName) {
Map<Object, EntityBeanIntercept> ebis = new HashMap<>();
for (EntityBeanIntercept ebi : list) {
for (EntityBeanIntercept ebi : batch) {
ebis.put(desc.cacheKeyForBean(ebi.getOwner()), ebi);
}
@@ -716,7 +716,6 @@ final class BeanDescriptorCacheHelp<T> {
if (beanLog.isTraceEnabled()) {
beanLog.trace(" load {}({}) - cache miss on property({})", cacheName, key, propertyName);
}
iterator.remove();
} else {
CachedBeanDataToBean.load(desc, ebi.getOwner(), cacheData, context);
loaded.add(ebi);
@@ -1,6 +1,7 @@
package io.ebeaninternal.server.deploy;
import io.ebeaninternal.server.query.SqlJoinType;
import io.ebeaninternal.server.query.SqlTreeJoin;
/**
* Used to provide context during sql construction.
@@ -133,4 +134,13 @@ public interface DbSqlContext {
*/
void appendFromForUpdate();
/**
* Delay adding an extra join to support inheritance discriminator in projection (IF required).
*/
void addExtraJoin(SqlTreeJoin treeJoin);
/**
* Add extra joins *IF* required to support inheritance discriminator in projection.
*/
void flushExtraJoins();
}
@@ -0,0 +1,67 @@
package io.ebeaninternal.server.executor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.*;
import java.util.concurrent.locks.ReentrantLock;
/**
* A "CachedThreadPool" based on Daemon threads.
* <p>
* The Threads are created as needed and once idle live for 60 seconds.
*/
final class DaemonExecutorService {
private static final Logger logger = LoggerFactory.getLogger(DaemonExecutorService.class);
private final ReentrantLock lock = new ReentrantLock(false);
private final String namePrefix;
private final int shutdownWaitSeconds;
private final ExecutorService service;
DaemonExecutorService(int shutdownWaitSeconds, String namePrefix) {
this.service = Executors.newCachedThreadPool(new DaemonThreadFactory(namePrefix));
this.shutdownWaitSeconds = shutdownWaitSeconds;
this.namePrefix = namePrefix;
}
<T> Future<T> submit(Callable<T> task) {
return service.submit(task);
}
Future<?> submit(Runnable task) {
return service.submit(task);
}
/**
* Shutdown this thread pool nicely if possible.
* <p>
* This will wait a maximum of 20 seconds before terminating any threads still working.
*/
void shutdown() {
lock.lock();
try {
if (service.isShutdown()) {
logger.debug("DaemonExecutorService[{}] already shut down", namePrefix);
return;
}
try {
logger.debug("DaemonExecutorService[{}] shutting down...", namePrefix);
service.shutdown();
if (!service.awaitTermination(shutdownWaitSeconds, TimeUnit.SECONDS)) {
logger.info("DaemonExecutorService[{}] shut down timeout exceeded. Terminating running threads.", namePrefix);
service.shutdownNow();
}
} catch (Exception e) {
logger.error("Error during shutdown of DaemonThreadPool[" + namePrefix + "]", e);
e.printStackTrace();
}
} finally {
lock.unlock();
}
}
}
@@ -1,15 +1,13 @@
package io.ebeaninternal.server.executor;
import io.avaje.lang.NonNullApi;
import io.ebean.config.BackgroundExecutorWrapper;
import io.ebeaninternal.api.SpiBackgroundExecutor;
import org.slf4j.MDC;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The default implementation of the BackgroundExecutor.
@@ -17,56 +15,81 @@ import java.util.concurrent.TimeUnit;
@NonNullApi
public final class DefaultBackgroundExecutor implements SpiBackgroundExecutor {
private final ScheduledExecutorService executor;
private static final Logger log = LoggerFactory.getLogger("io.ebean.BackgroundExecutor");
private final ScheduledExecutorService schedulePool;
private final DaemonExecutorService pool;
private final BackgroundExecutorWrapper wrapper;
/**
* Construct the default implementation of BackgroundExecutor.
*/
public DefaultBackgroundExecutor(int schedulePoolSize, int shutdownWaitSeconds, String namePrefix) {
this.executor = new DaemonScheduleThreadPool(schedulePoolSize, shutdownWaitSeconds, namePrefix);
public DefaultBackgroundExecutor(int schedulePoolSize, int shutdownWaitSeconds, String namePrefix, BackgroundExecutorWrapper wrapper) {
this.schedulePool = new DaemonScheduleThreadPool(schedulePoolSize, shutdownWaitSeconds, namePrefix + "-periodic-");
this.pool = new DaemonExecutorService(shutdownWaitSeconds, namePrefix);
this.wrapper = wrapper;
log.debug("Created backgroundExecutor {} (schedulePoolSize={}, shutdownWaitSeconds={})", namePrefix, schedulePoolSize, shutdownWaitSeconds);
}
/**
* Wrap the task with MDC context if defined.
*/
<T> Callable<T> wrapMDC(Callable<T> task) {
final Map<String, String> map = MDC.getCopyOfContextMap();
if (map == null) {
<T> Callable<T> wrap(Callable<T> task) {
if (wrapper == null) {
return task;
} else {
return () -> {
MDC.setContextMap(map);
try {
return task.call();
} finally {
MDC.clear();
}
};
return wrapper.wrap(task);
}
}
/**
* Wrap the task with MDC context if defined.
*/
Runnable wrapMDC(Runnable task) {
final Map<String, String> map = MDC.getCopyOfContextMap();
if (map == null) {
Runnable wrap(Runnable task) {
if (wrapper == null) {
return task;
} else {
return () -> {
MDC.setContextMap(map);
try {
return wrapper.wrap(task);
}
}
/**
* Decorates a runnable by adding an exception handler and some timing metrics.
* This is used in methods that accepts a <code>Runnable</code> and return
* either <code>void</code> or <code>ScheduledFuture</code>, as there is
* normally no Future.get() call.
*
* Note: When submitting a <code>Callable</code>, you must check
* <code>Future.get()</code> for exceptions.
*/
private Runnable logExceptions(Runnable task) {
long queued = System.nanoTime();
log.trace("Queued {}", task);
return () -> {
try {
if (log.isTraceEnabled()) {
long start = System.nanoTime();
log.trace("Start {} (delay time {} us)", task, (start - queued) / 1000L);
task.run();
log.trace("Stop {} (exec time {} us)", task, (System.nanoTime() - start) / 1000L);
} else {
task.run();
} finally {
MDC.clear();
}
};
}
} catch (Throwable t) {
// log any exception here. Note they will not bubble up to the calling user
// unless Future.get() is checked. (Which is almost never done on scheduled
// background executions)
log.error("Error while executing the task {}", task, t);
throw t;
}
};
}
@Override
public <T> Future<T> submit(Callable<T> task) {
return executor.submit(wrapMDC(task));
// Note: No "logExceptions" as we expect Future.get() by the invoker
return pool.submit(wrap(task));
}
/**
@@ -74,47 +97,52 @@ public final class DefaultBackgroundExecutor implements SpiBackgroundExecutor {
*/
@Override
public Future<?> submit(Runnable task) {
return executor.submit(wrapMDC(task));
return pool.submit(wrap(task));
}
@Override
public void execute(Runnable task) {
submit(task);
submit(logExceptions(task));
}
@Override
public void executePeriodically(Runnable task, long delay, TimeUnit unit) {
executor.scheduleWithFixedDelay(wrapMDC(task), delay, delay, unit);
schedulePool.scheduleWithFixedDelay(wrap(logExceptions(task)), delay, delay, unit);
}
@Override
public void executePeriodically(Runnable task, long initialDelay, long delay, TimeUnit unit) {
executor.scheduleWithFixedDelay(wrapMDC(task), initialDelay, delay, unit);
schedulePool.scheduleWithFixedDelay(wrap(logExceptions(task)), initialDelay, delay, unit);
}
@Override
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, long initialDelay, long delay, TimeUnit unit) {
return executor.scheduleWithFixedDelay(wrapMDC(task), initialDelay, delay, unit);
return schedulePool.scheduleWithFixedDelay(wrap(logExceptions(task)), initialDelay, delay, unit);
}
@Override
public ScheduledFuture<?> scheduleAtFixedRate(Runnable task, long initialDelay, long delay, TimeUnit unit) {
return executor.scheduleAtFixedRate(wrapMDC(task), initialDelay, delay, unit);
return schedulePool.scheduleAtFixedRate(wrap(logExceptions(task)), initialDelay, delay, unit);
}
@Override
public ScheduledFuture<?> schedule(Runnable task, long delay, TimeUnit unit) {
return executor.schedule(wrapMDC(task), delay, unit);
return schedulePool.schedule(wrap(logExceptions(task)), delay, unit);
}
@Override
public <V> ScheduledFuture<V> schedule(Callable<V> task, long delay, TimeUnit unit) {
return executor.schedule(wrapMDC(task), delay, unit);
// Note: No "logExceptions" as we expect Future.get() by the invoker
return schedulePool.schedule(wrap(task), delay, unit);
}
@Override
public void shutdown() {
executor.shutdown();
log.trace("BackgroundExecutor shutting down");
schedulePool.shutdown();
pool.shutdown();
log.debug("BackgroundExecutor stopped");
}
}
@@ -15,6 +15,7 @@ import io.ebeaninternal.server.deploy.BeanPropertyAssocMany;
import io.ebeaninternal.server.querydefn.OrmQueryProperties;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.locks.Lock;
@@ -66,7 +67,7 @@ final class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContext
}
}
protected void register(EntityBeanIntercept ebi) {
void register(EntityBeanIntercept ebi) {
if (currentBuffer.isFull()) {
currentBuffer = createBuffer(batchSize);
}
@@ -91,7 +92,7 @@ final class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContext
try {
if (bufferList != null) {
for (LoadBuffer loadBuffer : bufferList) {
if (!loadBuffer.list.isEmpty()) {
if (!loadBuffer.batch.isEmpty()) {
parent.getEbeanServer().loadBean(new LoadBeanRequest(loadBuffer, parentRequest));
}
if (forEach) {
@@ -115,13 +116,13 @@ final class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContext
private final ReentrantLock bufferLock = new ReentrantLock();
private final DLoadBeanContext context;
private final int batchSize;
private final List<EntityBeanIntercept> list;
private final Set<EntityBeanIntercept> batch;
private PersistenceContext persistenceContext;
LoadBuffer(DLoadBeanContext context, int batchSize) {
this.context = context;
this.batchSize = batchSize;
this.list = new ArrayList<>(batchSize);
this.batch = new HashSet<>(Math.max((int) (batchSize/.75f) + 1, 16));
}
@Override
@@ -139,7 +140,7 @@ final class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContext
* Return true if the buffer is full.
*/
public boolean isFull() {
return batchSize == list.size();
return batchSize == batch.size();
}
/**
@@ -150,12 +151,12 @@ final class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContext
// get persistenceContext from first loaded bean into the buffer
persistenceContext = ebi.getPersistenceContext();
}
list.add(ebi);
batch.add(ebi);
}
@Override
public List<EntityBeanIntercept> batch() {
return list;
public Set<EntityBeanIntercept> batch() {
return batch;
}
@Override
@@ -190,13 +191,13 @@ final class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContext
// lazy load property was a Many
return;
}
if (list.isEmpty()) {
if (!batch.contains(ebi)) {
// re-add to the batch and lazy load from DB skipping l2 cache
list.add(ebi);
batch.add(ebi);
} else if (context.hitCache) {
Set<EntityBeanIntercept> hits = context.desc.cacheBeanLoadAll(list, persistenceContext, ebi.getLazyLoadPropertyIndex(), ebi.getLazyLoadProperty());
list.removeAll(hits);
if (list.isEmpty() || hits.contains(ebi)) {
Set<EntityBeanIntercept> hits = context.desc.cacheBeanLoadAll(batch, persistenceContext, ebi.getLazyLoadPropertyIndex(), ebi.getLazyLoadProperty());
batch.removeAll(hits);
if (batch.isEmpty() || hits.contains(ebi)) {
// successfully hit the L2 cache so don't invoke DB lazy loading
return;
}
@@ -204,7 +205,7 @@ final class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContext
LoadBeanRequest req = new LoadBeanRequest(this, ebi, context.hitCache);
context.desc.ebeanServer().loadBean(req);
list.clear();
batch.clear();
}
}
@@ -7,6 +7,7 @@ import io.ebeaninternal.server.util.ArrayStack;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
final class DefaultDbSqlContext implements DbSqlContext {
@@ -43,7 +44,9 @@ final class DefaultDbSqlContext implements DbSqlContext {
private String currentPrefix;
private ArrayList<BeanProperty> encryptedProps;
private List<BeanProperty> encryptedProps;
private List<SqlTreeJoin> extraJoins;
private final CQueryDraftSupport draftSupport;
@@ -70,6 +73,24 @@ final class DefaultDbSqlContext implements DbSqlContext {
return alias.isIncludeSoftDelete();
}
@Override
public void addExtraJoin(SqlTreeJoin treeJoin) {
if (extraJoins == null) {
extraJoins = new ArrayList<>();
}
extraJoins.add(treeJoin);
}
@Override
public void flushExtraJoins() {
if (extraJoins != null) {
for (SqlTreeJoin extra : extraJoins) {
extra.addJoin(this);
}
extraJoins = null;
}
}
@Override
public void appendFromForUpdate() {
if (fromForUpdate != null) {
@@ -13,30 +13,22 @@ import java.util.Set;
final class SqlTree {
private final SqlTreeNode rootNode;
/**
* Property if resultSet contains master and detail rows.
*/
private final STreePropertyAssocMany manyProperty;
private final String distinctOn;
private final String selectSql;
private final String fromSql;
private final String groupBy;
/**
* Encrypted Properties require additional binding.
*/
private final STreeProperty[] encryptedProps;
/**
* Where clause for inheritance.
*/
private final String inheritanceWhereSql;
private final boolean noJoins;
/**
@@ -44,7 +36,6 @@ final class SqlTree {
*/
SqlTree(SqlTreeNode rootNode, String distinctOn, String selectSql, String fromSql, String groupBy, String inheritanceWhereSql,
STreeProperty[] encryptedProps, STreePropertyAssocMany manyProperty, boolean includeJoins) {
this.rootNode = rootNode;
this.distinctOn = distinctOn;
this.selectSql = selectSql;
@@ -15,22 +15,14 @@ import java.util.TreeSet;
final class SqlTreeAlias {
private final SpiQuery.TemporalMode temporalMode;
private int counter;
private int manyWhereCounter;
private final TreeSet<String> joinProps = new TreeSet<>();
private HashSet<String> embeddedPropertyJoins;
private final TreeSet<String> manyWhereJoinProps = new TreeSet<>();
private final HashMap<String, String> aliasMap = new HashMap<>();
private final HashMap<String, String> manyWhereAliasMap = new HashMap<>();
private final String rootTableAlias;
private int counter;
private int manyWhereCounter;
SqlTreeAlias(String rootTableAlias, SpiQuery.TemporalMode temporalMode) {
this.rootTableAlias = rootTableAlias;
@@ -224,8 +224,7 @@ public final class SqlTreeBuilder {
* Recursively build the query tree depending on what leaves in the tree
* should be included.
*/
private SqlTreeNode buildSelectChain(String prefix, STreePropertyAssoc prop,
STreeType desc, List<SqlTreeNode> joinList) {
private SqlTreeNode buildSelectChain(String prefix, STreePropertyAssoc prop, STreeType desc, List<SqlTreeNode> joinList) {
List<SqlTreeNode> myJoinList = new ArrayList<>();
List<STreePropertyAssocOne> extraProps = new ArrayList<>();
for (STreePropertyAssocOne one : desc.propsOne()) {
@@ -256,7 +255,7 @@ public final class SqlTreeBuilder {
}
addManyWhereJoins(myJoinList);
}
extraProps.forEach(props::add);
extraProps.forEach(props::addExtra);
if (!rawSql && manyWhereJoins.isFormulaWithJoin(prefix)) {
for (String property : manyWhereJoins.getFormulaJoinProperties(prefix)) {
@@ -0,0 +1,15 @@
package io.ebeaninternal.server.query;
import io.ebeaninternal.server.deploy.DbSqlContext;
/**
* Extra Sql joins conditionally added if required (after children are joined).
*/
public interface SqlTreeJoin {
/**
* Add the extra join if required.
*/
void addJoin(DbSqlContext ctx);
}
@@ -279,18 +279,16 @@ class SqlTreeNodeBean implements SqlTreeNode {
ctx.pushTableAlias(prefix);
baseTableAlias = ctx.getTableAlias(prefix);
// join and return SqlJoinType to use for child joins
joinType = appendFromBaseTable(ctx, joinType);
for (STreeProperty property : properties) {
// usually nothing... except for 1-1 Exported
property.appendFrom(ctx, joinType, null);
}
for (SqlTreeNode child : children) {
child.appendFrom(ctx, joinType);
}
ctx.flushExtraJoins();
ctx.popTableAlias();
ctx.popJoin();
}
@@ -33,6 +33,12 @@ public final class SqlTreeProperties {
propsList.addAll(Arrays.asList(props));
}
public void addExtra(STreePropertyAssocOne prop) {
if (!propsList.contains(prop)) {
add(prop);
}
}
public void add(STreeProperty prop) {
propsList.add(prop);
propNames.add(prop.name());
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.type;
import io.ebean.core.type.DataBinder;
import io.ebean.core.type.DataReader;
import java.sql.SQLException;
@@ -18,6 +19,15 @@ class ScalarTypeClob extends ScalarTypeStringBase {
super(true, Types.CLOB);
}
@Override
public void bind(DataBinder binder, String value) throws SQLException {
if (value == null) {
binder.setNull(Types.VARCHAR);
} else {
binder.setClob(value);
}
}
@Override
public String read(DataReader reader) throws SQLException {
return reader.getStringFromStream();
@@ -0,0 +1,56 @@
package io.ebeaninternal.server.executor;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Future;
import static org.assertj.core.api.Assertions.assertThat;
class DaemonExecutorServiceTest {
private final int count = 10;
private final int waitMillis = 100;
@Test
void submit() throws Exception {
DaemonExecutorService des = new DaemonExecutorService(5, "junk");
long start = System.currentTimeMillis();
List<Future<?>> futures = new ArrayList<>();
for (int i = 0; i < count; i++) {
futures.add(des.submit(this::doStuff));
}
for (Future<?> f: futures) {
f.get();
}
long exeMillis = System.currentTimeMillis() - start;
assertThat(exeMillis).isLessThan(count * waitMillis);
des.shutdown();
}
@Test
void submit_via_DefaultBackgroundExecutor() throws Exception {
DefaultBackgroundExecutor des = new DefaultBackgroundExecutor(1, 5, "junk", null);
long start = System.currentTimeMillis();
List<Future<?>> futures = new ArrayList<>();
for (int i = 0; i < count; i++) {
futures.add(des.submit(this::doStuff));
}
for (Future<?> f: futures) {
f.get();
}
long exeMillis = System.currentTimeMillis() - start;
assertThat(exeMillis).isLessThan(count * waitMillis);
des.shutdown();
}
private void doStuff() {
try {
Thread.sleep(waitMillis);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
+5 -5
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>12.15.1</version>
<version>12.16.0</version>
</parent>
<name>ebean ddl generation</name>
@@ -16,7 +16,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-ddl-runner</artifactId>
<version>1.2</version>
<version>${ebean-ddl-runner.version}</version>
</dependency>
<dependency>
@@ -28,14 +28,14 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core-type</artifactId>
<version>12.15.1</version>
<version>12.16.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>12.15.1</version>
<version>12.16.0</version>
<scope>provided</scope>
</dependency>
@@ -51,7 +51,7 @@
<dependency>
<groupId>io.avaje</groupId>
<artifactId>junit</artifactId>
<version>1.0</version>
<version>1.1</version>
<scope>test</scope>
</dependency>
@@ -0,0 +1,45 @@
package io.ebeaninternal.dbmigration.ddlgeneration;
import java.io.IOException;
/**
* Object that represents an Alter Table statements. Table alters are grouped together by tableName in DDL and can be extended by
* a ddl-specific handler (e.g. doing a reorg for DB2 or implement special grouping for Hana). There is one instance per table.
*
* @author TODO Roland Praml, FOCONIS AG
*/
public interface DdlAlterTable {
/**
* Writes the alter table statements to <code>target</code>
*/
void write(Appendable target) throws IOException;
/**
* Adds an alter table command for given column. When you invoke<br>
* <code>alterTable(writer, "my_table").add("alter column","my_column).append("type integer")</code> the resulting DDL (in
* standard implementation) would be<br>
* <code>alter table my_table alter column my_column type integer</code>
*
* @return a DdlBuffer, which can be used for further appends. Note you MUST NOT call <code>.endOfStatement()</code> on this
* buffer.
*/
DdlBuffer append(String operation, String columnName);
/**
* Adds a raw command. This is mainly used for executing user stored procedures.
*/
DdlBuffer raw(String string);
/**
* Flag that detects if history DDL (switching on) is handled for this table.
*/
boolean isHistoryHandled();
/**
* Sets the history handled flag for this table.
*/
void setHistoryHandled();
}
@@ -1,12 +1,28 @@
package io.ebeaninternal.dbmigration.ddlgeneration;
import java.io.IOException;
import java.util.Map;
import java.util.TreeMap;
import java.util.function.Function;
import io.ebeaninternal.dbmigration.ddlgeneration.platform.BaseDdlBuffer;
import io.ebeaninternal.dbmigration.model.MConfiguration;
import io.ebeaninternal.dbmigration.model.MTable;
import io.ebeaninternal.dbmigration.model.ModelContainer;
/**
* Write context holding the buffers for both apply and rollback DDL.
* Write context holding the buffers for both apply and rollback DDL. Description of the apply buffers:
* <ul>
* <li><b>applyDropDependencies:</b> Contains drops for foreign keys, indices, constraint or drop history table</li>
* <li><b>apply:</b> Contains &#64;DbMigraion.before, create table, create sequence or disable system versioning statements</li>
* <li><b>applyAlterTables:</b> Contains table alters (only that change the table data structure, there may be table alters like
* constraints etc. in postAlter)</li>
* <li><b>applyPostAlter:</b> Contains check constraints, unique constraints (which CAN be an index), column and table comments,
* &#64;DbMigraion.after, drop tables, drop sequences or enable system versioning statement</li>
* <li><b>applyForeignKeys: Contains foreign keys and indices.</b>
* <li><b>applyHistoryView:</b> The views for trigger based history support</li>
* <li><b>applyHistoryTrigger:</b> The triggers for trigger based history support</li>
* </ul>
*/
public class DdlWrite {
@@ -16,12 +32,12 @@ public class DdlWrite {
private final DdlBuffer apply = new BaseDdlBuffer();
private final Map<String, DdlAlterTable> applyAlterTables = new TreeMap<>();
private final DdlBuffer applyPostAlter = new BaseDdlBuffer();
private final DdlBuffer applyForeignKeys = new BaseDdlBuffer();
private final DdlBuffer applyHistoryView = new BaseDdlBuffer();
private final DdlBuffer applyHistoryTrigger = new BaseDdlBuffer();
private final DdlBuffer dropAllForeignKeys = new BaseDdlBuffer();
private final DdlBuffer dropAll = new BaseDdlBuffer();
@@ -66,12 +82,19 @@ public class DdlWrite {
*/
public boolean isApplyEmpty() {
return apply.getBuffer().isEmpty()
&& applyAlterTables.isEmpty()
&& applyPostAlter.getBuffer().isEmpty()
&& applyForeignKeys.getBuffer().isEmpty()
&& applyHistoryView.getBuffer().isEmpty()
&& applyHistoryTrigger.getBuffer().isEmpty()
&& applyDropDependencies.getBuffer().isEmpty();
}
/**
* Return the buffer that POST ALTER is written to.
*/
public DdlBuffer applyPostAlter() {
return applyPostAlter;
}
/**
* Return the buffer that APPLY DDL is written to.
*/
@@ -86,6 +109,20 @@ public class DdlWrite {
return applyDropDependencies;
}
/**
* Creates or returns the DdlAlterTable statement for <code>tablename</code>.
*
* Note: All alters on a particular table are sorted in natural order in the ddl script. This allows optimizing the alters by
* merging them or doing a reorg table after altering is done.
*
* @param tableName the table name
* @param factory the factory to construct a new object
* @return
*/
public DdlAlterTable applyAlterTable(String tableName, Function<String, DdlAlterTable> factory) {
return applyAlterTables.computeIfAbsent(tableName, factory);
}
/**
* Return the buffer that APPLY DDL is written to for foreign keys and their associated indexes.
* <p>
@@ -96,20 +133,6 @@ public class DdlWrite {
return applyForeignKeys;
}
/**
* Return the buffer that apply history-view DDL is written to.
*/
public DdlBuffer applyHistoryView() {
return applyHistoryView;
}
/**
* Return the buffer that apply history-trigger DDL is written to.
*/
public DdlBuffer applyHistoryTrigger() {
return applyHistoryTrigger;
}
/**
* Return the buffer used for the 'drop all DDL' for dropping foreign keys and associated indexes.
*/
@@ -124,4 +147,61 @@ public class DdlWrite {
return dropAll;
}
/**
* Writes the apply ddl to the target.
*/
public void writeApply(Appendable target) throws IOException {
if (!applyDropDependencies.isEmpty()) {
target.append("-- drop dependencies\n");
target.append(applyDropDependencies.getBuffer());
}
if (!apply.isEmpty()) {
target.append("-- apply changes\n");
target.append(apply.getBuffer());
}
if (!applyAlterTables.isEmpty()) {
target.append("-- apply alter tables\n");
for (DdlAlterTable alterTable : applyAlterTables.values()) {
alterTable.write(target);
}
}
if (!applyPostAlter.isEmpty()) {
target.append("-- apply post alter\n");
target.append(applyPostAlter.getBuffer());
}
if (!applyForeignKeys.isEmpty()) {
target.append("-- foreign keys and indices\n");
target.append(applyForeignKeys.getBuffer());
}
}
/**
* Writes the drop all ddl to the target.
*/
public void writeDropAll(Appendable target) throws IOException {
if (!dropAllForeignKeys.isEmpty()) {
target.append("-- drop all foreign keys\n");
target.append(dropAllForeignKeys.getBuffer());
}
if (!dropAll.isEmpty()) {
target.append("-- drop all\n");
target.append(dropAll.getBuffer());
}
}
/**
* Returns all create statements. Mainly used for unit-tests
*/
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
try {
writeDropAll(sb);
writeApply(sb);
} catch (IOException e) {
// can not happen
}
return sb.toString();
}
}
@@ -1,13 +1,20 @@
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
import io.ebean.config.DatabaseConfig;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.config.dbplatform.DbPlatformType;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlAlterTable;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlHandler;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite;
import io.ebeaninternal.dbmigration.migration.AlterColumn;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -17,16 +24,13 @@ public abstract class AbstractHanaDdl extends PlatformDdl {
public AbstractHanaDdl(DatabasePlatform platform) {
super(platform);
this.addColumn = "add (";
this.addColumnSuffix = ")";
this.alterColumn = "alter (";
this.alterColumnSuffix = ")";
this.columnDropDefault = " default null";
this.columnSetDefault = " default";
this.columnSetNotnull = " not null";
this.addColumn = "add";
this.alterColumn = "alter";
this.columnDropDefault = "default null";
this.columnSetDefault = "default";
this.columnSetNotnull = "not null";
this.columnSetNull = " null";
this.dropColumn = "drop (";
this.dropColumnSuffix = ")";
this.dropColumn = "drop";
this.dropConstraintIfExists = "drop constraint ";
this.dropIndexIfExists = "drop index ";
this.dropSequenceIfExists = "drop sequence ";
@@ -38,7 +42,7 @@ public abstract class AbstractHanaDdl extends PlatformDdl {
}
@Override
public String alterColumnBaseAttributes(AlterColumn alter) {
public void alterColumn(DdlWrite writer, AlterColumn alter) {
String tableName = alter.getTableName();
String columnName = alter.getColumnName();
String currentType = alter.getCurrentType();
@@ -51,49 +55,21 @@ public abstract class AbstractHanaDdl extends PlatformDdl {
: (alter.getDefaultValue() != null ? alter.getDefaultValue() : alter.getCurrentDefaultValue());
String defaultValueClause = (defaultValue == null || defaultValue.isEmpty()) ? "" : " default " + defaultValue;
DdlBuffer buffer = new BaseDdlBuffer();
if (!isConvertible(currentType, type)) {
// add an intermediate conversion if possible
if (isNumberType(currentType)) {
// numbers can always be converted to decimal
buffer.append("alter table ").append(tableName).append(" ").append(alterColumn).append(" ").append(columnName)
.append(" decimal ").append(notnullClause).append(alterColumnSuffix)
.endOfStatement();
alterTable(writer, tableName).append(alterColumn, columnName).append("decimal").append(notnullClause);
} else if (isStringType(currentType)) {
// strings can always be converted to nclob
// Note: we do not add default clause here to avoid error[SAP DBTech JDBC: [336]: invalid default value:
// default value cannot be created on column of data type NCLOB
buffer.append("alter table ").append(tableName).append(" ").append(alterColumn).append(" ").append(columnName)
.append(" nclob").append(notnullClause).append(alterColumnSuffix)
.endOfStatement();
alterTable(writer, tableName).append(alterColumn, columnName).append("nclob").append(notnullClause);
}
}
buffer.append("alter table ").append(tableName).append(" ").append(alterColumn).append(" ").append(columnName)
.append(" ").append(type).append(defaultValueClause).append(notnullClause).append(alterColumnSuffix);
return buffer.getBuffer();
}
@Override
public String alterColumnDefaultValue(String tableName, String columnName, String defaultValue) {
throw new UnsupportedOperationException();
}
@Override
public String alterColumnNotnull(String tableName, String columnName, boolean notnull) {
return null;
}
@Override
public DdlHandler createDdlHandler(DatabaseConfig config) {
return new HanaDdlHandler(config, this);
}
@Override
public String alterColumnType(String tableName, String columnName, String type) {
return null;
alterTable(writer, tableName).append(alterColumn, columnName).append(type).append(defaultValueClause).append(notnullClause);
}
@Override
@@ -140,9 +116,8 @@ public abstract class AbstractHanaDdl extends PlatformDdl {
* foreign keys. That's why we call a user stored procedure here
*/
@Override
public void alterTableDropColumn(DdlBuffer buffer, String tableName, String columnName) {
buffer.append("CALL usp_ebean_drop_column('").append(tableName).append("', '").append(columnName).append("')")
.endOfStatement();
public void alterTableDropColumn(DdlWrite writer, String tableName, String columnName) {
alterTable(writer, tableName).raw("CALL usp_ebean_drop_column('").append(tableName).append("', '").append(columnName).append("')");
}
/**
@@ -216,4 +191,71 @@ public abstract class AbstractHanaDdl extends PlatformDdl {
return type != null
&& (type.startsWith("varchar") || type.startsWith("nvarchar") || "clob".equals(type) || "nclob".equals(type));
}
@Override
protected DdlAlterTable alterTable(DdlWrite writer, String tableName) {
return writer.applyAlterTable(tableName, HanaAlterTableWrite::new);
}
/**
* Joins alter table commands and add open/closing brackets for the alter statements
*/
private static class HanaAlterTableWrite extends BaseAlterTableWrite {
public HanaAlterTableWrite(String tableName) {
super(tableName);
}
@Override
protected List<AlterCmd> postProcessCommands(List<AlterCmd> cmds) {
List<AlterCmd> newCmds = new ArrayList<>();
Map<String, List<AlterCmd>> batches = new LinkedHashMap<>();
Set<String> columns = new HashSet<>();
for (AlterCmd cmd : cmds) {
switch (cmd.getOperation()) {
case "add":
case "alter":
case "drop":
if (cmd.getColumn() != null && !columns.add(cmd.getColumn())) {
// column already seen
flushBatches(newCmds, batches);
columns.clear();
}
batches.computeIfAbsent(cmd.getOperation(), k -> new ArrayList<>()).add(cmd);
break;
default:
flushBatches(newCmds, batches);
columns.clear();
newCmds.add(cmd);
}
}
flushBatches(newCmds, batches);
return newCmds;
}
/**
* Merges add/alter/drop commands into one statement.
*/
private void flushBatches(List<AlterCmd> newCmds, Map<String, List<AlterCmd>> batches) {
for (Entry<String, List<AlterCmd>> entry : batches.entrySet()) {
AlterCmd raw = newRawCommand("alter table ").append(tableName()).append(" ")
.append(entry.getKey()).append(" (");
List<AlterCmd> cmds = entry.getValue();
for (int i = 0; i < cmds.size(); i++) {
AlterCmd cmd = cmds.get(i);
if (i > 0) {
raw.append(",\n ");
}
raw.append(cmd.getColumn());
if (!cmd.getAlternation().isEmpty()) {
raw.append(" ").append(cmd.getAlternation());
}
}
raw.append(")");
newCmds.add(raw);
}
batches.clear();
}
}
}
@@ -0,0 +1,152 @@
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlAlterTable;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer;
/**
* Contains alter statements per table.
*
* @author Roland Praml, FOCONIS AG
*/
public class BaseAlterTableWrite implements DdlAlterTable {
protected static final String RAW_OPERATION = "$RAW";
public class AlterCmd {
// the command (e.g. "alter", "modify"
private final String operation;
// the affected column (note: each column can only be altered once on MariaDB)
private final String column;
private final DdlBuffer alternationBuffer = new BaseDdlBuffer() {
@Override
public DdlBuffer endOfStatement() {
throw new UnsupportedOperationException();
};
};
protected AlterCmd(String operation, String column) {
this.operation = operation;
this.column = column;
}
public AlterCmd append(String content) {
alternationBuffer.append(content);
return this;
}
public String getOperation() {
return operation;
}
public String getColumn() {
return column;
}
public String getAlternation() {
return alternationBuffer.getBuffer();
}
protected void write(Appendable target) throws IOException {
if (operation.equals(RAW_OPERATION)) {
// this is a raw command. e.g. an USP call. Must be done in the correct order
// of all alter commands
target.append(getAlternation());
} else {
target.append("alter table ").append(tableName).append(' ').append(operation);
if (column != null) {
target.append(' ').append(column);
}
if (!getAlternation().isEmpty()) {
target.append(' ').append(getAlternation());
}
}
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
try {
write(sb);
} catch (IOException e) {
throw new RuntimeException(e);
}
return sb.toString();
}
}
private final String tableName;
private List<AlterCmd> cmds = new ArrayList<>();
private boolean historyHandled;
public BaseAlterTableWrite(String tableName) {
this.tableName = tableName;
}
public String tableName() {
return tableName;
}
protected AlterCmd newRawCommand(String sql) {
AlterCmd cmd = new AlterCmd(RAW_OPERATION, null);
cmd.alternationBuffer.append(sql);
return cmd;
}
public AlterCmd newOperation(String operation, String column) {
return new AlterCmd(operation, column);
}
/**
* Adds a statement. The statement is prefixed with "alter table TABLENAME" and may be batched, if platform supports this. The
* returned StringBuilder can be used, to complete the statement
*/
@Override
public DdlBuffer append(String operation, String column) {
AlterCmd cmd = new AlterCmd(operation, column);
cmds.add(cmd);
return cmd.alternationBuffer;
}
@Override
public DdlBuffer raw(String sql) {
AlterCmd cmd = newRawCommand(sql);
cmds.add(cmd);
return cmd.alternationBuffer;
}
/**
* Method can be overwritten to return a new list of commands. The given list must not be modified, but a new command list with
* modified commands can be returned (e.g. to handle DB2 reorg or special syntax in Hana)
*/
protected List<AlterCmd> postProcessCommands(List<AlterCmd> cmds) {
return cmds;
}
/**
* Writes the DDL to <code>target</code>.
*/
@Override
public void write(Appendable target) throws IOException {
for (AlterCmd cmd : postProcessCommands(Collections.unmodifiableList(cmds))) {
cmd.write(target);
target.append(";\n");
}
}
@Override
public boolean isHistoryHandled() {
return historyHandled;
}
@Override
public void setHistoryHandled() {
historyHandled = true;
}
}
@@ -11,7 +11,6 @@ import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlOptions;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite;
import io.ebeaninternal.dbmigration.ddlgeneration.TableDdl;
import io.ebeaninternal.dbmigration.ddlgeneration.platform.util.IndexSet;
import io.ebeaninternal.dbmigration.migration.AddColumn;
import io.ebeaninternal.dbmigration.migration.AddHistoryTable;
import io.ebeaninternal.dbmigration.migration.AddTableComment;
@@ -34,9 +33,7 @@ import io.ebeaninternal.server.deploy.IdentityMode;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import static io.ebean.util.StringHelper.replace;
import static io.ebeaninternal.api.PlatformMatch.matchPlatform;
@@ -47,12 +44,6 @@ import static io.ebeaninternal.dbmigration.ddlgeneration.platform.SplitColumns.s
*/
public class BaseTableDdl implements TableDdl {
enum HistorySupport {
NONE,
SQL2011,
TRIGGER_BASED
}
protected final DbConstraintNaming naming;
protected final NamingConvention namingConvention;
@@ -68,15 +59,9 @@ public class BaseTableDdl implements TableDdl {
protected final List<UniqueConstraint> externalCompoundUnique = new ArrayList<>();
/**
* Base tables that have associated history tables that need their triggers/functions regenerated as
* columns have been added, removed, included or excluded.
*/
protected final Map<String, HistoryTableUpdate> regenerateHistoryTriggers = new LinkedHashMap<>();
private final boolean strictMode;
private final HistorySupport historySupport;
private final boolean alterHistoryTables;
/**
* Helper class that is used to execute the migration ddl before and after the migration action.
@@ -127,29 +112,29 @@ public class BaseTableDdl implements TableDdl {
after = getScriptsForPlatform(alter.getAfter());
}
void writeBefore(DdlBuffer buffer) {
void write(DdlWrite writer) {
if (!before.isEmpty()) {
buffer.end();
}
if (!before.isEmpty() && withHistory) {
buffer.append("-- NOTE: table has @History - special migration may be necessary").newLine();
}
for (String ddlScript : before) {
buffer.appendStatement(translate(ddlScript, tableName, columnName, defaultValue));
}
}
void writeAfter(DdlBuffer buffer) {
if (!after.isEmpty() && withHistory) {
buffer.append("-- NOTE: table has @History - special migration may be necessary").newLine();
}
// here we run post migration scripts
for (String ddlScript : after) {
buffer.appendStatement(translate(ddlScript, tableName, columnName, defaultValue));
writer.apply().end();
if (withHistory) {
writer.apply().append("-- NOTE: table has @History - special migration may be necessary").newLine();
}
for (String ddlScript : before) {
writer.apply().appendStatement(translate(ddlScript, tableName, columnName, defaultValue));
}
}
// effective order in DDL.sql:
// 'before' goes to apply-buffer
// altered columns will be in the alterTable buffers.
// 'after' goes to the post-alter-buffer
if (!after.isEmpty()) {
buffer.end();
writer.applyPostAlter().append("-- NOTE: table has @History - special migration may be necessary").newLine();
// here we run post migration scripts
for (String ddlScript : after) {
writer.applyPostAlter().appendStatement(translate(ddlScript, tableName, columnName, defaultValue));
}
writer.applyPostAlter().end();
}
}
@@ -197,9 +182,9 @@ public class BaseTableDdl implements TableDdl {
this.strictMode = config.isDdlStrictMode();
DbHistorySupport hist = platformDdl.getPlatform().getHistorySupport();
if (hist == null) {
this.historySupport = HistorySupport.NONE;
this.alterHistoryTables = false;
} else {
this.historySupport = hist.isStandardsBased() ? HistorySupport.SQL2011 : HistorySupport.TRIGGER_BASED;
this.alterHistoryTables = platformDdl.historyDdl.alterHistoryTables();
}
}
@@ -248,7 +233,7 @@ public class BaseTableDdl implements TableDdl {
}
}
if (platformDdl.isInlineForeignKeys()) {
writeInlineForeignKeys(writer, createTable);
writeInlineForeignKeys(apply, createTable);
}
apply.newLine().append(")");
addTableStorageEngine(apply, createTable);
@@ -340,7 +325,8 @@ public class BaseTableDdl implements TableDdl {
uqName = col.getUnique();
}
String[] columnNames = {col.getName()};
writer.apply().appendStatement(platformDdl.alterTableAddUniqueConstraint(tableName, uqName, columnNames, Boolean.TRUE.equals(col.isNotnull()) ? null : columnNames));
String[] nullableColumns = Boolean.TRUE.equals(col.isNotnull()) ? null : columnNames;
writer.applyPostAlter().appendStatement(platformDdl.alterTableAddUniqueConstraint(tableName, uqName, columnNames, nullableColumns));
writer.dropAllForeignKeys().appendStatement(platformDdl.dropIndex(uqName, tableName));
}
@@ -349,7 +335,7 @@ public class BaseTableDdl implements TableDdl {
String[] columnNames = split(constraint.getColumnNames());
String[] nullableColumns = split(constraint.getNullableColumns());
writer.apply().appendStatement(platformDdl.alterTableAddUniqueConstraint(tableName, uqName, columnNames, nullableColumns));
writer.applyPostAlter().appendStatement(platformDdl.alterTableAddUniqueConstraint(tableName, uqName, columnNames, nullableColumns));
writer.dropAllForeignKeys().appendStatement(platformDdl.dropIndex(uqName, tableName));
}
}
@@ -368,25 +354,25 @@ public class BaseTableDdl implements TableDdl {
platformDdl.createWithHistory(writer, table);
}
protected void writeInlineForeignKeys(DdlWrite writer, CreateTable createTable) {
protected void writeInlineForeignKeys(DdlBuffer buffer, CreateTable createTable) {
for (Column column : createTable.getColumn()) {
String references = column.getReferences();
if (hasValue(references)) {
writeInlineForeignKey(writer, column);
writeInlineForeignKey(buffer, column);
}
}
writeInlineCompoundForeignKeys(writer, createTable);
writeInlineCompoundForeignKeys(buffer, createTable);
}
protected void writeInlineForeignKey(DdlWrite writer, Column column) {
protected void writeInlineForeignKey(DdlBuffer buffer, Column column) {
String fkConstraint = platformDdl.tableInlineForeignKey(new WriteForeignKey(null, column));
writer.apply().append(",").newLine().append(" ").append(fkConstraint);
buffer.append(",").newLine().append(" ").append(fkConstraint);
}
protected void writeInlineCompoundForeignKeys(DdlWrite writer, CreateTable createTable) {
protected void writeInlineCompoundForeignKeys(DdlBuffer buffer, CreateTable createTable) {
for (ForeignKey key : createTable.getForeignKey()) {
String fkConstraint = platformDdl.tableInlineForeignKey(new WriteForeignKey(null, key));
writer.apply().append(",").newLine().append(" ").append(fkConstraint);
buffer.append(",").newLine().append(" ").append(fkConstraint);
}
}
@@ -558,7 +544,7 @@ public class BaseTableDdl implements TableDdl {
@Override
public void generate(DdlWrite writer, CreateIndex index) {
if (platformInclude(index.getPlatforms())) {
writer.apply().appendStatement(platformDdl.createIndex(new WriteCreateIndex(index)));
writer.applyForeignKeys().appendStatement(platformDdl.createIndex(new WriteCreateIndex(index)));
writer.dropAll().appendStatement(platformDdl.dropIndex(index.getIndexName(), index.getTableName(), Boolean.TRUE.equals(index.isConcurrent())));
}
}
@@ -566,7 +552,8 @@ public class BaseTableDdl implements TableDdl {
@Override
public void generate(DdlWrite writer, DropIndex dropIndex) {
if (platformInclude(dropIndex.getPlatforms())) {
writer.apply().appendStatement(platformDdl.dropIndex(dropIndex.getIndexName(), dropIndex.getTableName(), Boolean.TRUE.equals(dropIndex.isConcurrent())));
writer.applyDropDependencies()
.appendStatement(platformDdl.dropIndex(dropIndex.getIndexName(), dropIndex.getTableName(), Boolean.TRUE.equals(dropIndex.isConcurrent())));
}
}
@@ -574,12 +561,14 @@ public class BaseTableDdl implements TableDdl {
public void generate(DdlWrite writer, AddUniqueConstraint constraint) {
if (platformInclude(constraint.getPlatforms())) {
if (DdlHelp.isDropConstraint(constraint.getColumnNames())) {
writer.apply().appendStatement(platformDdl.alterTableDropUniqueConstraint(constraint.getTableName(), constraint.getConstraintName()));
writer.applyDropDependencies()
.appendStatement(platformDdl.alterTableDropUniqueConstraint(constraint.getTableName(), constraint.getConstraintName()));
} else {
String[] cols = split(constraint.getColumnNames());
String[] nullableColumns = split(constraint.getNullableColumns());
writer.apply().appendStatement(platformDdl.alterTableAddUniqueConstraint(constraint.getTableName(), constraint.getConstraintName(), cols, nullableColumns));
writer.applyPostAlter()
.appendStatement(platformDdl.alterTableAddUniqueConstraint(constraint.getTableName(), constraint.getConstraintName(), cols, nullableColumns));
}
}
}
@@ -587,9 +576,9 @@ public class BaseTableDdl implements TableDdl {
@Override
public void generate(DdlWrite writer, AlterForeignKey alterForeignKey) {
if (DdlHelp.isDropForeignKey(alterForeignKey.getColumnNames())) {
writer.apply().appendStatement(platformDdl.alterTableDropForeignKey(alterForeignKey.getTableName(), alterForeignKey.getName()));
writer.applyDropDependencies().appendStatement(platformDdl.alterTableDropForeignKey(alterForeignKey.getTableName(), alterForeignKey.getName()));
} else {
writer.apply().appendStatement(platformDdl.alterTableAddForeignKey(writer.getOptions(), new WriteForeignKey(alterForeignKey)));
writer.applyForeignKeys().appendStatement(platformDdl.alterTableAddForeignKey(writer.getOptions(), new WriteForeignKey(alterForeignKey)));
}
}
@@ -619,22 +608,13 @@ public class BaseTableDdl implements TableDdl {
*/
@Override
public void generateEpilog(DdlWrite writer) {
if (!regenerateHistoryTriggers.isEmpty()) {
platformDdl.lockTables(writer.applyHistoryTrigger(), regenerateHistoryTriggers.keySet());
for (HistoryTableUpdate update : this.regenerateHistoryTriggers.values()) {
platformDdl.regenerateHistoryTriggers(writer, update);
}
platformDdl.unlockTables(writer.applyHistoryTrigger(), regenerateHistoryTriggers.keySet());
}
platformDdl.generateEpilog(writer);
}
@Override
public void generate(DdlWrite writer, AddTableComment addTableComment) {
if (hasValue(addTableComment.getComment())) {
platformDdl.addTableComment(writer.apply(), addTableComment.getName(), addTableComment.getComment());
platformDdl.addTableComment(writer.applyPostAlter(), addTableComment.getName(), addTableComment.getComment());
}
}
@@ -646,14 +626,16 @@ public class BaseTableDdl implements TableDdl {
String tableName = addColumn.getTableName();
List<Column> columns = addColumn.getColumn();
for (Column column : columns) {
alterTableAddColumn(writer.apply(), tableName, column, false, isTrue(addColumn.isWithHistory()));
alterTableAddColumn(writer, tableName, column, false, isTrue(addColumn.isWithHistory()));
}
if (isTrue(addColumn.isWithHistory()) && historySupport == HistorySupport.TRIGGER_BASED) {
if (isTrue(addColumn.isWithHistory())) {
platformDdl.regenerateHistoryTriggers(writer, tableName);
// make same changes to the history table
String historyTable = historyTable(tableName);
for (Column column : columns) {
regenerateHistoryTriggers(tableName, HistoryTableUpdate.Change.ADD, column.getName());
alterTableAddColumn(writer.apply(), historyTable, column, true, true);
if (alterHistoryTables) {
String historyTable = historyTable(tableName);
for (Column column : columns) {
alterTableAddColumn(writer, historyTable, column, true, true);
}
}
}
for (Column column : columns) {
@@ -661,7 +643,6 @@ public class BaseTableDdl implements TableDdl {
writeForeignKey(writer, tableName, column);
}
}
writer.apply().end();
}
/**
@@ -669,14 +650,14 @@ public class BaseTableDdl implements TableDdl {
*/
@Override
public void generate(DdlWrite writer, DropTable dropTable) {
dropTable(writer.apply(), dropTable.getName());
dropTable(writer.applyPostAlter(), dropTable.getName());
if (hasValue(dropTable.getSequenceCol())
&& platformDdl.getPlatform().getDbIdentity().isSupportsSequence()) {
String sequenceName = dropTable.getSequenceName();
if (!hasValue(sequenceName)) {
sequenceName = namingConvention.getSequenceName(dropTable.getName(), dropTable.getSequenceCol());
}
dropSequence(writer.apply(), sequenceName);
dropSequence(writer.applyPostAlter(), sequenceName);
}
}
@@ -686,14 +667,15 @@ public class BaseTableDdl implements TableDdl {
@Override
public void generate(DdlWrite writer, DropColumn dropColumn) {
String tableName = dropColumn.getTableName();
alterTableDropColumn(writer.apply(), tableName, dropColumn.getColumnName());
alterTableDropColumn(writer, tableName, dropColumn.getColumnName());
if (isTrue(dropColumn.isWithHistory()) && historySupport == HistorySupport.TRIGGER_BASED) {
if (isTrue(dropColumn.isWithHistory())) {
platformDdl.regenerateHistoryTriggers(writer, tableName);
// also drop from the history table
regenerateHistoryTriggers(tableName, HistoryTableUpdate.Change.DROP, dropColumn.getColumnName());
alterTableDropColumn(writer.apply(), historyTable(tableName), dropColumn.getColumnName());
if (alterHistoryTables) {
alterTableDropColumn(writer, historyTable(tableName), dropColumn.getColumnName());
}
}
writer.apply().end();
}
/**
@@ -702,12 +684,13 @@ public class BaseTableDdl implements TableDdl {
@Override
public void generate(DdlWrite writer, AlterColumn alterColumn) {
DdlMigrationHelp ddlHelp = new DdlMigrationHelp(alterColumn);
ddlHelp.writeBefore(writer.apply());
ddlHelp.write(writer);
if (isTrue(alterColumn.isHistoryExclude())) {
regenerateHistoryTriggers(alterColumn.getTableName(), HistoryTableUpdate.Change.EXCLUDE, alterColumn.getColumnName());
} else if (isFalse(alterColumn.isHistoryExclude())) {
regenerateHistoryTriggers(alterColumn.getTableName(), HistoryTableUpdate.Change.INCLUDE, alterColumn.getColumnName());
if (alterColumn.isHistoryExclude() != null) {
// CHECKME should we drop a small comment, if historyExclude changes, as not all DB systems will support this
// writer.apply().append("-- historyExclude of ").append(alterColumn.getTableName()).append(".").append(alterColumn.getColumnName())
// .append(" altered to ").append(String.valueOf(alterColumn.isHistoryExclude())).end();
platformDdl.regenerateHistoryTriggers(writer, alterColumn.getTableName());
}
if (hasValue(alterColumn.getDropForeignKey())) {
@@ -739,31 +722,21 @@ public class BaseTableDdl implements TableDdl {
// drop constraint before altering type etc
dropCheckConstraint(writer, alterColumn, alterColumn.getCheckConstraintName());
}
boolean alterBaseAttributes = false;
if (hasValue(alterColumn.getType())) {
alterColumnType(writer, alterColumn);
alterBaseAttributes = true;
}
if (hasValue(alterColumn.getDefaultValue())) {
alterColumnDefaultValue(writer, alterColumn);
alterBaseAttributes = true;
}
if (alterColumn.isNotnull() != null) {
alterColumnNotnull(writer, alterColumn);
alterBaseAttributes = true;
}
if (alterBaseAttributes) {
alterColumnBaseAttributes(writer, alterColumn);
if (hasValue(alterColumn.getType())
|| hasValue(alterColumn.getDefaultValue())
|| alterColumn.isNotnull() != null) {
alterColumn(writer, alterColumn);
}
if (alterCheckConstraint) {
// add constraint last (after potential type change)
addCheckConstraint(writer, alterColumn);
}
ddlHelp.writeAfter(writer.apply());
}
private void alterColumnComment(DdlWrite writer, AlterColumn alterColumn) {
platformDdl.addColumnComment(writer.apply(), alterColumn.getTableName(), alterColumn.getColumnName(), alterColumn.getComment());
platformDdl.addColumnComment(writer.applyPostAlter(), alterColumn.getTableName(), alterColumn.getColumnName(), alterColumn.getComment());
}
/**
@@ -774,76 +747,72 @@ public class BaseTableDdl implements TableDdl {
}
/**
* Register the base table that we need to regenerate the history triggers on.
* alter all the base attributes (type/default/notnull) of the column together.
* Some platforms (like mysql/sqlserver/hana) must do that in one statement,
* other platforms may use several statements for altering one of the base
* attributes.
*/
protected void regenerateHistoryTriggers(String baseTableName, HistoryTableUpdate.Change change, String column) {
HistoryTableUpdate update = regenerateHistoryTriggers.computeIfAbsent(baseTableName, HistoryTableUpdate::new);
update.add(change, column);
}
protected void alterColumn(DdlWrite writer, AlterColumn alter) {
platformDdl.alterColumn(writer, alter);
/**
* This is mysql specific - alter all the base attributes of the column together.
* Will be called, if there is a type, dbdefault or notnull change.
*/
protected void alterColumnBaseAttributes(DdlWrite writer, AlterColumn alter) {
String ddl = platformDdl.alterColumnBaseAttributes(alter);
if (hasValue(ddl)) {
writer.apply().appendStatement(ddl);
if (isTrue(alter.isWithHistory()) && alter.getType() != null && historySupport == HistorySupport.TRIGGER_BASED) {
// mysql and sql server column type change allowing nulls in the history table column
regenerateHistoryTriggers(alter.getTableName(), HistoryTableUpdate.Change.ALTER, alter.getColumnName());
AlterColumn alterHistoryColumn = new AlterColumn();
alterHistoryColumn.setTableName(historyTable(alter.getTableName()));
alterHistoryColumn.setColumnName(alter.getColumnName());
alterHistoryColumn.setType(alter.getType());
String histColumnDdl = platformDdl.alterColumnBaseAttributes(alterHistoryColumn);
// write the apply to history table
writer.apply().appendStatement(histColumnDdl);
if (isTrue(alter.isWithHistory())) {
// we will apply only type changes or notNull -> null transition
boolean isNull = Boolean.FALSE.equals(alter.isNotnull());
boolean applyToHistory = alter.getType() != null || isNull;
boolean syncExact = platformDdl.getPlatform().isPlatform(Platform.DB2);
// DB2 needs exact sync for notnull/null
if (syncExact && alter.isNotnull() != null) {
applyToHistory = true;
}
if (applyToHistory) {
platformDdl.regenerateHistoryTriggers(writer, alter.getTableName());
if (alterHistoryTables) {
AlterColumn alterHistoryColumn = new AlterColumn();
alterHistoryColumn.setTableName(historyTable(alter.getTableName()));
alterHistoryColumn.setColumnName(alter.getColumnName());
// ignore default value (not needed on history tables)
alterHistoryColumn.setCurrentType(alter.getCurrentType());
alterHistoryColumn.setType(alter.getType());
if (syncExact) {
alterHistoryColumn.setCurrentNotnull(alter.isCurrentNotnull());
alterHistoryColumn.setNotnull(alter.isNotnull());
} else if (isNull) {
// do transition from notNull to null
alterHistoryColumn.setCurrentNotnull(Boolean.TRUE);
alterHistoryColumn.setNotnull(Boolean.FALSE);
} else {
// assume that the column contains null values from the past (required for platforms like mysql/sqlserver/hana)
alterHistoryColumn.setCurrentNotnull(Boolean.FALSE);
}
platformDdl.alterColumn(writer, alterHistoryColumn);
}
}
}
}
protected void alterColumnDefaultValue(DdlWrite writer, AlterColumn alter) {
writer.apply().appendStatement(platformDdl.alterColumnDefaultValue(alter.getTableName(), alter.getColumnName(), alter.getDefaultValue()));
}
protected void dropCheckConstraint(DdlWrite writer, AlterColumn alter, String constraintName) {
writer.apply().appendStatement(platformDdl.alterTableDropConstraint(alter.getTableName(), constraintName));
writer.applyDropDependencies()
.appendStatement(platformDdl.alterTableDropConstraint(alter.getTableName(), constraintName));
}
protected void addCheckConstraint(DdlWrite writer, AlterColumn alter) {
writer.apply().appendStatement(platformDdl.alterTableAddCheckConstraint(alter.getTableName(), alter.getCheckConstraintName(), alter.getCheckConstraint()));
writer.applyPostAlter()
.appendStatement(platformDdl.alterTableAddCheckConstraint(alter.getTableName(), alter.getCheckConstraintName(), alter.getCheckConstraint()));
}
protected void alterColumnNotnull(DdlWrite writer, AlterColumn alter) {
writer.apply().appendStatement(platformDdl.alterColumnNotnull(alter.getTableName(), alter.getColumnName(), alter.isNotnull()));
}
protected void alterColumnType(DdlWrite writer, AlterColumn alter) {
String ddl = platformDdl.alterColumnType(alter.getTableName(), alter.getColumnName(), alter.getType());
if (hasValue(ddl)) {
writer.apply().appendStatement(ddl);
if (isTrue(alter.isWithHistory()) && historySupport == HistorySupport.TRIGGER_BASED) {
regenerateHistoryTriggers(alter.getTableName(), HistoryTableUpdate.Change.ALTER, alter.getColumnName());
// apply same type change to matching column in the history table
ddl = platformDdl.alterColumnType(historyTable(alter.getTableName()), alter.getColumnName(), alter.getType());
writer.apply().appendStatement(ddl);
}
}
}
protected void alterColumnAddForeignKey(DdlWrite writer, AlterColumn alterColumn) {
alterTableAddForeignKey(writer.getOptions(), writer.apply(), new WriteForeignKey(alterColumn));
alterTableAddForeignKey(writer.getOptions(), writer.applyForeignKeys(), new WriteForeignKey(alterColumn));
}
protected void alterColumnDropForeignKey(DdlWrite writer, AlterColumn alter) {
writer.apply().appendStatement(platformDdl.alterTableDropForeignKey(alter.getTableName(), alter.getDropForeignKey()));
writer.applyDropDependencies()
.appendStatement(platformDdl.alterTableDropForeignKey(alter.getTableName(), alter.getDropForeignKey()));
}
protected void alterColumnDropUniqueConstraint(DdlWrite writer, AlterColumn alter) {
writer.apply().appendStatement(platformDdl.alterTableDropUniqueConstraint(alter.getTableName(), alter.getDropUnique()));
writer.applyDropDependencies()
.appendStatement(platformDdl.alterTableDropUniqueConstraint(alter.getTableName(), alter.getDropUnique()));
}
protected void alterColumnAddUniqueOneToOneConstraint(DdlWrite writer, AlterColumn alter) {
@@ -857,31 +826,29 @@ public class BaseTableDdl implements TableDdl {
protected void addUniqueConstraint(DdlWrite writer, AlterColumn alter, String uqName) {
String[] cols = {alter.getColumnName()};
boolean notNull = alter.isNotnull() != null ? alter.isNotnull() : Boolean.TRUE.equals(alter.isNotnull());
writer.apply().appendStatement(platformDdl.alterTableAddUniqueConstraint(alter.getTableName(), uqName, cols, notNull ? null : cols));
writer.applyPostAlter()
.appendStatement(platformDdl.alterTableAddUniqueConstraint(alter.getTableName(), uqName, cols, notNull ? null : cols));
writer.dropAllForeignKeys().appendStatement(platformDdl.dropIndex(uqName, alter.getTableName()));
}
protected void alterTableDropColumn(DdlBuffer buffer, String tableName, String columnName) {
platformDdl.alterTableDropColumn(buffer, tableName, columnName);
protected void alterTableDropColumn(DdlWrite writer, String tableName, String columnName) {
platformDdl.alterTableDropColumn(writer, tableName, columnName);
}
protected void alterTableAddColumn(DdlBuffer buffer, String tableName, Column column, boolean onHistoryTable, boolean withHistory) {
protected void alterTableAddColumn(DdlWrite writer, String tableName, Column column, boolean onHistoryTable, boolean withHistory) {
DdlMigrationHelp help = new DdlMigrationHelp(tableName, column, withHistory);
if (!onHistoryTable) {
help.writeBefore(buffer);
help.write(writer);
}
platformDdl.alterTableAddColumn(buffer, tableName, column, onHistoryTable, help.getDefaultValue());
platformDdl.alterTableAddColumn(writer, tableName, column, onHistoryTable, help.getDefaultValue());
final String comment = column.getComment();
if (comment != null && !comment.isEmpty()) {
platformDdl.addColumnComment(buffer, tableName, column.getName(), comment);
platformDdl.addColumnComment(writer.applyPostAlter(), tableName, column.getName(), comment);
}
if (!onHistoryTable) {
help.writeAfter(buffer);
}
}
protected boolean isFalse(Boolean value) {
@@ -14,6 +14,7 @@ public class ClickHouseDdl extends PlatformDdl {
super(platform);
this.includeStorageEngine = true;
this.identitySuffix = "";
this.columnNotNull = null;
}
@Override
@@ -68,11 +69,6 @@ public class ClickHouseDdl extends PlatformDdl {
return null;
}
@Override
protected void writeColumnNotNull(DdlBuffer buffer) {
// do nothing
}
@Override
public void addTableComment(DdlBuffer apply, String tableName, String tableComment) {
// do nothing
@@ -1,7 +1,14 @@
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
import io.ebean.annotation.ConstraintMode;
import java.util.ArrayList;
import java.util.List;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.util.StringHelper;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlAlterTable;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite;
import io.ebeaninternal.dbmigration.migration.Column;
/**
* DB2 platform specific DDL.
@@ -12,12 +19,13 @@ public class DB2Ddl extends PlatformDdl {
super(platform);
this.dropTableIfExists = "drop table ";
this.dropSequenceIfExists = "drop sequence ";
this.dropConstraintIfExists = "drop constraint";
this.dropIndexIfExists = "drop index ";
this.dropConstraintIfExists = "NOT USED";
this.dropIndexIfExists = "NOT USED";
this.identitySuffix = " generated by default as identity";
this.columnSetNull = "drop not null";
this.columnSetType = "set data type ";
this.inlineUniqueWhenNullable = false;
this.historyDdl = new Db2HistoryDdl();
}
@Override
@@ -30,7 +38,7 @@ public class DB2Ddl extends PlatformDdl {
throw new NullPointerException();
}
StringBuilder sb = new StringBuilder("create unique index ");
sb.append(uqName).append(" on ").append(tableName).append('(');
sb.append(maxConstraintName(uqName)).append(" on ").append(tableName).append('(');
for (int i = 0; i < columns.length; i++) {
if (i > 0) {
@@ -43,8 +51,143 @@ public class DB2Ddl extends PlatformDdl {
}
@Override
protected void appendForeignKeyOnUpdate(StringBuilder buffer, ConstraintMode mode) {
// do nothing, no on update clause for db2
public void alterTableAddColumn(DdlWrite writer, String tableName, Column column, boolean onHistoryTable, String defaultValue) {
String convertedType = convert(column.getType());
DdlBuffer buffer = alterTable(writer, tableName).append(addColumn, column.getName());
buffer.append(convertedType);
// Add default value also to history table if it is not excluded
if (defaultValue != null) {
buffer.append(" default ");
buffer.append(defaultValue);
}
if (isTrue(column.isNotnull())) {
buffer.appendWithSpace(columnNotNull);
}
// DB2 History table must match exact!
if (!onHistoryTable) {
// check constraints cannot be added in one statement for h2
if (!StringHelper.isNull(column.getCheckConstraint())) {
String ddl = alterTableAddCheckConstraint(tableName, column.getCheckConstraintName(), column.getCheckConstraint());
writer.applyPostAlter().appendStatement(ddl);
}
}
}
@Override
public String alterTableDropForeignKey(String tableName, String fkName) {
return alterTableDropConstraint(tableName, fkName);
};
@Override
public String alterTableDropUniqueConstraint(String tableName, String uniqueConstraintName) {
return alterTableDropConstraint(tableName, uniqueConstraintName)
+ "\n" + dropIndex(uniqueConstraintName, tableName);
}
@Override
public String alterTableDropConstraint(String tableName, String constraintName) {
StringBuilder sb = new StringBuilder(300);
sb.append("delimiter $$\n")
.append("begin\n")
.append("if exists (select constname from syscat.tabconst where tabschema = current_schema and constname = '")
.append(maxConstraintName(constraintName).toUpperCase())
.append("' and tabname = '").append(lowerTableName(tableName).toUpperCase()).append("') then\n")
.append(" prepare stmt from 'alter table ").append(lowerTableName(tableName))
.append(" drop constraint ").append(maxConstraintName(constraintName)).append("';\n")
.append(" execute stmt;\n")
.append("end if;\n")
.append("end$$");
return sb.toString();
}
@Override
public String dropIndex(String indexName, String tableName, boolean concurrent) {
StringBuilder sb = new StringBuilder(300);
sb.append("delimiter $$\n")
.append("begin\n")
.append("if exists (select indname from syscat.indexes where indschema = current_schema and indname = '")
.append(maxConstraintName(indexName).toUpperCase()).append("') then\n")
.append(" prepare stmt from 'drop index ").append(maxConstraintName(indexName)).append("';\n")
.append(" execute stmt;\n")
.append("end if;\n")
.append("end$$");
return sb.toString();
}
@Override
public String dropSequence(String sequenceName) {
StringBuilder sb = new StringBuilder(300);
sb.append("delimiter $$\n");
sb.append("begin\n");
sb.append("if exists (select seqschema from syscat.sequences where seqschema = current_schema and seqname = '")
.append(maxConstraintName(sequenceName).toUpperCase()).append("') then\n");
sb.append(" prepare stmt from 'drop sequence ").append(maxConstraintName(sequenceName)).append("';\n");
sb.append(" execute stmt;\n");
sb.append("end if;\n");
sb.append("end$$");
return sb.toString();
}
@Override
protected DdlAlterTable alterTable(DdlWrite writer, String tableName) {
return writer.applyAlterTable(tableName, Db2AlterTableWrite::new);
};
static class Db2AlterTableWrite extends BaseAlterTableWrite {
public Db2AlterTableWrite(String tableName) {
super(tableName);
}
@Override
protected List<AlterCmd> postProcessCommands(List<AlterCmd> cmds) {
List<AlterCmd> ret = new ArrayList<>(cmds.size() + 1);
boolean requiresReorg = false;
for (AlterCmd cmd : cmds) {
ret.add(cmd);
if (!requiresReorg && checkReorg(cmd)) {
requiresReorg = true;
}
}
if (requiresReorg) {
ret.add(newRawCommand("call sysproc.admin_cmd('reorg table " + tableName() + "')"));
}
return ret;
}
/**
* determine, if we need a reorg.
*
* See: https://www.ibm.com/docs/en/db2/11.5?topic=statements-alter-table The following is the full list of REORG-recommended
* ALTER statements that cause a version change and place the table into a REORG-pending state:
* <ul>
* <li>DROP COLUMN
* <li>ALTER COLUMN SET NOT NULL
* <li>ALTER COLUMN DROP NOT NULL
* <li>ALTER COLUMN SET DATA TYPE, except in the following situations:<br>
* Increasing the length of a VARCHAR or VARGRAPHIC column<br>
* Decreasing the length of a VARCHAR or VARGRAPHIC column without truncating trailing blanks from existing data, when no indexes
* exist on the column
* </ul>
*
*/
private boolean checkReorg(AlterCmd cmd) {
switch (cmd.getOperation()) {
case "drop column":
return true;
case "alter column":
String alter = cmd.getAlternation();
return alter.equals("set not null")
|| alter.equals("drop not default")
|| alter.startsWith("set data type"); // note: altering varchar length only is not detected here
default:
return false;
}
}
}
}
@@ -0,0 +1,138 @@
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
import java.util.Collection;
import io.ebean.config.DatabaseConfig;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlAlterTable;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite;
import io.ebeaninternal.dbmigration.migration.AddHistoryTable;
import io.ebeaninternal.dbmigration.migration.DropHistoryTable;
import io.ebeaninternal.dbmigration.model.MColumn;
import io.ebeaninternal.dbmigration.model.MTable;
/**
* DB2 History support.
*
* @author Roland Praml, FOCONIS AG
*/
public class Db2HistoryDdl implements PlatformHistoryDdl {
private String systemPeriodStart;
private String systemPeriodEnd;
private String transactionId;
private PlatformDdl platformDdl;
private String historySuffix;
@Override
public void configure(DatabaseConfig config, PlatformDdl platformDdl) {
this.systemPeriodStart = config.getAsOfSysPeriod() + "_start";
this.systemPeriodEnd = config.getAsOfSysPeriod() + "_end";
this.transactionId = config.getAsOfSysPeriod() + "_txn"; // required for DB2
this.platformDdl = platformDdl;
this.historySuffix = config.getHistoryTableSuffix();
}
@Override
public void createWithHistory(DdlWrite writer, MTable table) {
String tableName = table.getName();
String historyTableName = tableName + historySuffix;
DdlBuffer apply = writer.applyPostAlter();
apply.append(platformDdl.getCreateTableCommandPrefix()).append(" ").append(historyTableName).append(" (").newLine();
// create history table
Collection<MColumn> cols = table.allColumns();
for (MColumn column : cols) {
if (!column.isDraftOnly()) {
writeColumnDefinition(apply, column.getName(), column.getType(), column.isNotnull() || column.isPrimaryKey());
apply.append(",").newLine();
}
}
writeColumnDefinition(apply, systemPeriodStart, "timestamp(12)", true);
apply.append(",").newLine();
writeColumnDefinition(apply, systemPeriodEnd, "timestamp(12)", true);
apply.append(",").newLine();
writeColumnDefinition(apply, transactionId, "timestamp(12)", false);
apply.newLine().append(")").endOfStatement();
// enable system versioning
addSysPeriodColumns(writer, tableName);
enableSystemVersioning(apply, tableName);
platformDdl.alterTable(writer, tableName).setHistoryHandled();
// drop all: We do not drop columns here, as the whole table will be dropped
disableSystemVersioning(writer.dropAll(), tableName);
writer.dropAll().append("drop table ").append(historyTableName).endOfStatement();
}
void addSysPeriodColumns(DdlWrite writer, String baseTable) {
platformDdl.alterTableAddColumn(writer, baseTable, systemPeriodStart, "timestamp(12) not null generated always as row begin", null);
platformDdl.alterTableAddColumn(writer, baseTable, systemPeriodEnd, "timestamp(12) not null generated always as row end", null);
platformDdl.alterTableAddColumn(writer, baseTable, transactionId, "timestamp(12) generated always as transaction start id", null);
platformDdl.alterTable(writer, baseTable).append("add period system_time", null)
.append("(").append(systemPeriodStart).append(",").append(systemPeriodEnd).append(")");
}
@Override
public void dropHistoryTable(DdlWrite writer, DropHistoryTable dropHistoryTable) {
dropHistoryTable(writer, dropHistoryTable.getBaseTable(), dropHistoryTable.getBaseTable() + historySuffix);
}
protected void dropHistoryTable(DdlWrite writer, String baseTable, String historyTable) {
disableSystemVersioning(writer.apply(), baseTable);
writer.apply().append("alter table ").append(baseTable).append(" drop period system_time").endOfStatement();
// drop the period & period columns
platformDdl.alterTableDropColumn(writer, baseTable, systemPeriodStart);
platformDdl.alterTableDropColumn(writer, baseTable, systemPeriodEnd);
platformDdl.alterTableDropColumn(writer, baseTable, transactionId);
// drop the history table
writer.applyPostAlter().append("drop table ").append(historyTable).endOfStatement();
}
@Override
public void addHistoryTable(DdlWrite writer, AddHistoryTable addHistoryTable) {
MTable table = writer.getTable(addHistoryTable.getBaseTable());
if (table == null) {
throw new IllegalStateException("MTable " + addHistoryTable.getBaseTable() + " not found in writer? (required for history DDL)");
}
createWithHistory(writer, table);
}
@Override
public boolean alterHistoryTables() {
return true;
}
@Override
public void updateTriggers(DdlWrite writer, String tableName) {
DdlAlterTable alter = platformDdl.alterTable(writer, tableName);
MTable table = writer.getTable(tableName);
if (table.isWithHistory() && !alter.isHistoryHandled()) {
disableSystemVersioning(writer.apply(), tableName);
enableSystemVersioning(writer.applyPostAlter(), tableName);
alter.setHistoryHandled();
}
}
protected void writeColumnDefinition(DdlBuffer buffer, String columnName, String type, boolean isNotNull) {
String platformType = platformDdl.convert(type);
buffer.append(" ").append(platformDdl.lowerColumnName(columnName));
buffer.append(" ").append(platformType);
if (isNotNull) {
buffer.append(" not null");
}
}
public void disableSystemVersioning(DdlBuffer apply, String tableName) {
apply.append("alter table ").append(tableName).append(" drop versioning").endOfStatement();
}
public void enableSystemVersioning(DdlBuffer apply, String tableName) {
apply.append("alter table ").append(tableName).append(" add versioning use history table ").append(tableName).append(historySuffix).endOfStatement();
}
}
@@ -2,6 +2,7 @@ package io.ebeaninternal.dbmigration.ddlgeneration.platform;
import io.ebean.config.DatabaseConfig;
import io.ebean.config.DbConstraintNaming;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlAlterTable;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite;
import io.ebeaninternal.dbmigration.migration.AddHistoryTable;
@@ -46,40 +47,6 @@ public abstract class DbTriggerBasedHistoryDdl implements PlatformHistoryDdl {
this.sysPeriodEnd = sysPeriod + "_end";
}
@Override
public void updateTriggers(DdlWrite writer, HistoryTableUpdate update) {
MTable table = writer.getTable(update.getBaseTable());
if (table == null) {
throw new IllegalStateException("MTable " + update.getBaseTable() + " not found in writer? (required for history DDL)");
}
updateTriggers(writer, table, update);
}
/**
* Replace the existing triggers/stored procedures/views for history table support given the included columns.
*/
protected abstract void updateHistoryTriggers(DbTriggerUpdate triggerUpdate);
/**
* Process the HistoryTableUpdate which can result in changes to the apply, rollback
* and drop scripts.
*/
protected void updateTriggers(DdlWrite writer, MTable table, HistoryTableUpdate update) {
writer.applyHistoryTrigger().append("-- changes: ").append(update.description()).newLine();
updateHistoryTriggers(createDbTriggerUpdate(writer, table));
}
protected DbTriggerUpdate createDbTriggerUpdate(DdlWrite writer, MTable table) {
List<String> columns = columnNamesForApply(table);
String baseTableName = table.getName();
String historyTableName = historyTableName(baseTableName);
return new DbTriggerUpdate(baseTableName, historyTableName, writer, columns);
}
@Override
public void dropHistoryTable(DdlWrite writer, DropHistoryTable dropHistoryTable) {
@@ -87,7 +54,10 @@ public abstract class DbTriggerBasedHistoryDdl implements PlatformHistoryDdl {
// drop in appropriate order
dropTriggers(writer.applyDropDependencies(), baseTable);
dropHistoryTableEtc(writer.applyDropDependencies(), baseTable);
dropWithHistoryView(writer.applyDropDependencies(), baseTable);
dropHistoryTable(writer.applyDropDependencies(), baseTable);
dropSysPeriodColumns(writer, baseTable);
}
@Override
@@ -106,23 +76,51 @@ public abstract class DbTriggerBasedHistoryDdl implements PlatformHistoryDdl {
public void createWithHistory(DdlWrite writer, MTable table) {
String baseTable = table.getName();
String whenCreatedColumn = table.getWhenCreatedColumn();
addSysPeriodColumns(writer, baseTable, table.getWhenCreatedColumn());
createHistoryTable(writer.applyPostAlter(), table);
createWithHistoryView(writer.applyPostAlter(), table.getName());
createTriggers(writer.applyPostAlter(), baseTable, columnNamesForApply(table));
writer.applyPostAlter().end();
// drop all scripts
dropTriggers(writer.dropAll(), baseTable);
dropHistoryTableEtc(writer.dropAll(), baseTable);
dropWithHistoryView(writer.dropAll(), baseTable);
dropHistoryTable(writer.dropAll(), baseTable);
// no need to dropSysPeriodColumns as whole table will be deleted soon
addHistoryTable(writer, table, whenCreatedColumn);
createStoredFunction(writer, table);
createTriggers(writer, table);
}
protected abstract void createTriggers(DdlWrite writer, MTable table);
@Override
public void updateTriggers(DdlWrite writer, String tableName) {
MTable table = writer.getTable(tableName);
if (table != null && table.isWithHistory()) {
DdlAlterTable alter = platformDdl.alterTable(writer, tableName);
if (!alter.isHistoryHandled()) {
// this code effectively disables history support before the table alter and enables it again
// immediately after the table alter. As all alters per table are altogether now, this can done here
dropTriggers(writer.apply(), tableName);
dropWithHistoryView(writer.apply(), tableName);
// here are the alter commands
createWithHistoryView(writer.applyPostAlter(), tableName);
createTriggers(writer.applyPostAlter(), tableName, columnNamesForApply(table));
alter.setHistoryHandled();
}
}
}
/**
* Will add a history trigger to the buffer. The config
*
* @param buffer
* @param table
*/
protected abstract void createTriggers(DdlBuffer buffer, String baseTable, List<String> columnNames);
protected abstract void dropTriggers(DdlBuffer buffer, String baseTable);
protected void createStoredFunction(DdlWrite writer, MTable table) {
// do nothing
}
protected String normalise(String tableName) {
return constraintNaming.normaliseTable(tableName);
@@ -152,22 +150,19 @@ public abstract class DbTriggerBasedHistoryDdl implements PlatformHistoryDdl {
String baseTableName = table.getName();
DdlBuffer apply = writer.applyHistoryView();
addSysPeriodColumns(apply, baseTableName, whenCreatedColumn);
createHistoryTable(apply, table);
createWithHistoryView(apply, baseTableName);
addSysPeriodColumns(writer, baseTableName, whenCreatedColumn);
createHistoryTable(writer.applyPostAlter(), table);
createWithHistoryView(writer.applyPostAlter(), baseTableName);
}
protected void addSysPeriodColumns(DdlBuffer apply, String baseTableName, String whenCreatedColumn) {
apply.append("alter table ").append(baseTableName).append(" add column ")
.append(sysPeriodStart).append(" ").append(sysPeriodType).append(" default ").append(now).endOfStatement();
apply.append("alter table ").append(baseTableName).append(" add column ")
.append(sysPeriodEnd).append(" ").append(sysPeriodType).endOfStatement();
protected void addSysPeriodColumns(DdlWrite writer, String baseTableName, String whenCreatedColumn) {
platformDdl.alterTableAddColumn(writer, baseTableName, sysPeriodStart, sysPeriodType, now);
platformDdl.alterTableAddColumn(writer, baseTableName, sysPeriodEnd, sysPeriodType, null);
if (whenCreatedColumn != null) {
apply.append("update ").append(baseTableName).append(" set ").append(sysPeriodStart).append(" = ").append(whenCreatedColumn).endOfStatement();
writer.applyPostAlter()
.append("update ").append(baseTableName).append(" set ").append(sysPeriodStart).append(" = ").append(whenCreatedColumn).endOfStatement();
}
}
@@ -210,22 +205,7 @@ public abstract class DbTriggerBasedHistoryDdl implements PlatformHistoryDdl {
.append("create view ").append(baseTableName).append(viewSuffix)
.append(" as select * from ").append(baseTableName)
.append(" union all select * from ").append(baseTableName).append(historySuffix)
.endOfStatement().end();
}
/**
* For postgres/h2/mysql we need to drop and recreate the view. Well, we could add columns to the end of the view
* but otherwise we need to drop and create it.
*/
protected void recreateHistoryView(DbTriggerUpdate update) {
DdlBuffer buffer = update.dropDependencyBuffer();
// we need to drop the view early/first before any changes to the tables etc
buffer.append("drop view if exists ").append(update.getBaseTable()).append(viewSuffix).endOfStatement();
// recreate the view after all ddl modifications - the view requires ALL columns, also the historyExclude ones.
createWithHistoryView(update.historyViewBuffer(), update.getBaseTable());
.endOfStatement();
}
protected void appendSysPeriodColumns(DdlBuffer apply, String prefix) {
@@ -233,21 +213,22 @@ public abstract class DbTriggerBasedHistoryDdl implements PlatformHistoryDdl {
appendColumnName(apply, prefix, sysPeriodEnd);
}
protected void dropHistoryTableEtc(DdlBuffer buffer, String baseTableName) {
buffer.append("drop view ").append(baseTableName).append(viewSuffix).endOfStatement();
dropSysPeriodColumns(buffer, baseTableName);
buffer.append("drop table ").append(baseTableName).append(historySuffix).endOfStatement().end();
protected void dropWithHistoryView(DdlBuffer apply, String baseTableName) {
apply.append("drop view ").append(baseTableName).append(viewSuffix).endOfStatement();
}
protected void dropSysPeriodColumns(DdlBuffer buffer, String baseTableName) {
platformDdl.alterTableDropColumn(buffer, baseTableName, sysPeriodStart);
platformDdl.alterTableDropColumn(buffer, baseTableName, sysPeriodEnd);
protected void dropHistoryTable(DdlBuffer apply, String baseTableName) {
apply.append("drop table ").append(baseTableName).append(historySuffix).endOfStatement().end();
}
protected void appendInsertIntoHistory(DdlBuffer buffer, String historyTable, List<String> columns) {
protected void dropSysPeriodColumns(DdlWrite writer, String baseTableName) {
platformDdl.alterTableDropColumn(writer, baseTableName, sysPeriodStart);
platformDdl.alterTableDropColumn(writer, baseTableName, sysPeriodEnd);
}
buffer.append(" insert into ").append(historyTable).append(" (").append(sysPeriodStart).append(",").append(sysPeriodEnd).append(",");
protected void appendInsertIntoHistory(DdlBuffer buffer, String baseTable, List<String> columns) {
buffer.append(" insert into ").append(baseTable).append(historySuffix).append(" (").append(sysPeriodStart).append(",").append(sysPeriodEnd).append(",");
appendColumnNames(buffer, columns, "");
buffer.append(") values (OLD.").append(sysPeriodStart).append(", ").append(sysPeriodEndValue).append(",");
appendColumnNames(buffer, columns, "OLD.");
@@ -285,4 +266,8 @@ public abstract class DbTriggerBasedHistoryDdl implements PlatformHistoryDdl {
return table.allHistoryColumns(true);
}
@Override
public boolean alterHistoryTables() {
return true;
}
}
@@ -1,71 +0,0 @@
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite;
import java.util.List;
/**
* DB trigger update when a change occurs on a table with history.
*/
public class DbTriggerUpdate {
private final String baseTableName;
private final String historyTableName;
private final DdlWrite writer;
private final List<String> columns;
public DbTriggerUpdate(String baseTableName, String historyTableName, DdlWrite writer, List<String> columns) {
this.baseTableName = baseTableName;
this.historyTableName = historyTableName;
this.writer = writer;
this.columns = columns;
}
/**
* Return the appropriate buffer for the current mode.
*/
public DdlBuffer historyViewBuffer() {
return writer.applyHistoryView();
}
/**
* Return the appropriate buffer for the current mode.
*/
public DdlBuffer historyTriggerBuffer() {
return writer.applyHistoryTrigger();
}
/**
* Return the appropriate drop dependency buffer for the current mode.
*/
public DdlBuffer dropDependencyBuffer() {
return writer.applyDropDependencies();
}
/**
* Return the base table name.
*/
public String getBaseTable() {
return baseTableName;
}
/**
* Return the history table name.
*/
public String getHistoryTable() {
return historyTableName;
}
/**
* Return the included columns.
*/
public List<String> getColumns() {
return columns;
}
}
@@ -1,8 +1,8 @@
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
import java.util.List;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite;
import io.ebeaninternal.dbmigration.model.MTable;
/**
* H2 history support using DB triggers to maintain a history table.
@@ -23,18 +23,8 @@ public class H2HistoryDdl extends DbTriggerBasedHistoryDdl {
}
@Override
protected void createTriggers(DdlWrite writer, MTable table) {
String baseTableName = table.getName();
DdlBuffer apply = writer.applyHistoryTrigger();
addCreateTrigger(apply, updateTriggerName(baseTableName), baseTableName);
}
@Override
protected void updateHistoryTriggers(DbTriggerUpdate update) {
recreateHistoryView(update);
DdlBuffer buffer = update.historyTriggerBuffer();
dropTriggers(buffer, update.getBaseTable());
addCreateTrigger(buffer, updateTriggerName(update.getBaseTable()), update.getBaseTable());
protected void createTriggers(DdlBuffer buffer, String baseTable, List<String> columnNames) {
addCreateTrigger(buffer, updateTriggerName(baseTable), baseTable);
}
private void addCreateTrigger(DdlBuffer apply, String triggerName, String baseTable) {
@@ -1,11 +0,0 @@
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
import io.ebean.config.DatabaseConfig;
import io.ebeaninternal.dbmigration.ddlgeneration.BaseDdlHandler;
public class HanaDdlHandler extends BaseDdlHandler {
public HanaDdlHandler(DatabaseConfig config, PlatformDdl platformDdl) {
super(config, platformDdl, new HanaTableDdl(config, platformDdl));
}
}
@@ -1,6 +1,7 @@
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
import io.ebean.config.DatabaseConfig;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlAlterTable;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite;
import io.ebeaninternal.dbmigration.migration.AddHistoryTable;
@@ -9,9 +10,6 @@ import io.ebeaninternal.dbmigration.model.MColumn;
import io.ebeaninternal.dbmigration.model.MTable;
import java.util.Collection;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
public class HanaHistoryDdl implements PlatformHistoryDdl {
@@ -19,8 +17,6 @@ public class HanaHistoryDdl implements PlatformHistoryDdl {
private String systemPeriodEnd;
private PlatformDdl platformDdl;
private String historySuffix;
private final AtomicInteger counter = new AtomicInteger(0);
private Map<String, String> createdHistoryTables = new ConcurrentHashMap<>();
@Override
public void configure(DatabaseConfig config, PlatformDdl platformDdl) {
@@ -34,10 +30,7 @@ public class HanaHistoryDdl implements PlatformHistoryDdl {
public void createWithHistory(DdlWrite writer, MTable table) {
String tableName = table.getName();
String historyTableName = tableName + historySuffix;
DdlBuffer apply = writer.applyHistoryView();
if (apply.isEmpty()) {
createdHistoryTables.clear();
}
DdlBuffer apply = writer.applyPostAlter();
apply.append(platformDdl.getCreateTableCommandPrefix()).append(" ").append(historyTableName).append(" (").newLine();
@@ -64,9 +57,8 @@ public class HanaHistoryDdl implements PlatformHistoryDdl {
apply.append("alter table ").append(tableName).append(" add period for system_time(").append(systemPeriodStart)
.append(",").append(systemPeriodEnd).append(")").endOfStatement();
enableSystemVersioning(apply, tableName, historyTableName, true, false);
createdHistoryTables.put(tableName, historyTableName);
enableSystemVersioning(apply, tableName, true);
platformDdl.alterTable(writer, tableName).setHistoryHandled();
dropHistoryTable(writer.dropAll(), tableName, historyTableName);
}
@@ -101,8 +93,19 @@ public class HanaHistoryDdl implements PlatformHistoryDdl {
}
@Override
public void updateTriggers(DdlWrite writer, HistoryTableUpdate baseTable) {
// nothing to do
public boolean alterHistoryTables() {
return true;
}
@Override
public void updateTriggers(DdlWrite writer, String tableName) {
DdlAlterTable alter = platformDdl.alterTable(writer, tableName);
MTable table = writer.getTable(tableName);
if (table.isWithHistory() && !alter.isHistoryHandled()) {
disableSystemVersioning(writer.apply(), tableName);
enableSystemVersioning(writer.applyPostAlter(), tableName, false);
alter.setHistoryHandled();
}
}
protected void writeColumnDefinition(DdlBuffer buffer, String columnName, String type, String defaultValue,
@@ -123,34 +126,15 @@ public class HanaHistoryDdl implements PlatformHistoryDdl {
}
public void disableSystemVersioning(DdlBuffer apply, String tableName) {
disableSystemVersioning(apply, tableName, false);
apply.append("alter table ").append(tableName).append(" drop system versioning").endOfStatement();
}
public void disableSystemVersioning(DdlBuffer apply, String tableName, boolean uniqueStatement) {
apply.append("alter table ").append(tableName).append(" drop system versioning");
if (uniqueStatement) {
// needed for the DB migration test to prevent the statement from being filtered
// out as a duplicate
apply.append(" /* ").append(String.valueOf(counter.getAndIncrement())).append(" */");
}
apply.endOfStatement();
}
public void enableSystemVersioning(DdlBuffer apply, String tableName, String historyTableName, boolean validated,
boolean uniqueStatement) {
apply.append("alter table ").append(tableName).append(" add system versioning history table ").append(historyTableName);
public void enableSystemVersioning(DdlBuffer apply, String tableName, boolean validated) {
apply.append("alter table ").append(tableName).append(" add system versioning history table ").append(tableName).append(historySuffix);
if (!validated) {
apply.append(" not validated");
}
if (uniqueStatement) {
// needed for the DB migration test to prevent the statement from being filtered
// out as a duplicate
apply.append(" /* ").append(String.valueOf(counter.getAndIncrement())).append(" */");
}
apply.endOfStatement();
}
public boolean isSystemVersioningEnabled(String tableName) {
return !createdHistoryTables.containsKey(tableName);
}
}
@@ -1,130 +0,0 @@
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
import io.ebean.config.DatabaseConfig;
import io.ebean.config.PropertiesWrapper;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite;
import io.ebeaninternal.dbmigration.migration.AddColumn;
import io.ebeaninternal.dbmigration.migration.AlterColumn;
import io.ebeaninternal.dbmigration.migration.Column;
import io.ebeaninternal.dbmigration.migration.DropColumn;
import io.ebeaninternal.dbmigration.model.MTable;
import java.util.List;
public class HanaTableDdl extends BaseTableDdl {
private final HanaHistoryDdl historyDdl;
private final boolean generateUniqueDdl;
public HanaTableDdl(DatabaseConfig config, PlatformDdl platformDdl) {
super(config, platformDdl);
this.historyDdl = (HanaHistoryDdl) platformDdl.historyDdl;
if (config.getProperties() != null) {
PropertiesWrapper wrapper = new PropertiesWrapper("ebean", "hana", config.getProperties(), config.getClassLoadConfig());
this.generateUniqueDdl = wrapper.getBoolean("generateUniqueDdl", false);
} else {
this.generateUniqueDdl = false;
}
}
@Override
protected void alterColumnDefaultValue(DdlWrite writer, AlterColumn alter) {
// done in alterColumnBaseAttributes
}
@Override
public void generate(DdlWrite writer, AddColumn addColumn) {
String tableName = addColumn.getTableName();
MTable table = writer.getTable(tableName);
if (table == null) {
super.generate(writer, addColumn);
return;
}
boolean manageSystemVersioning = isTrue(table.isWithHistory()) && historyDdl.isSystemVersioningEnabled(tableName);
if (manageSystemVersioning) {
historyDdl.disableSystemVersioning(writer.apply(), table.getName(), this.generateUniqueDdl);
}
super.generate(writer, addColumn);
if (manageSystemVersioning) {
// make same changes to the history table
String historyTable = historyTable(tableName);
List<Column> columns = addColumn.getColumn();
for (Column column : columns) {
alterTableAddColumn(writer.apply(), historyTable, column, true, true);
}
historyDdl.enableSystemVersioning(writer.apply(), table.getName(), historyTable, false, this.generateUniqueDdl);
}
}
@Override
public void generate(DdlWrite writer, AlterColumn alterColumn) {
String tableName = alterColumn.getTableName();
MTable table = writer.getTable(tableName);
if (table == null) {
super.generate(writer, alterColumn);
return;
}
boolean manageSystemVersioning = isTrue(table.isWithHistory()) && historyDdl.isSystemVersioningEnabled(tableName);
if (manageSystemVersioning) {
historyDdl.disableSystemVersioning(writer.apply(), tableName, this.generateUniqueDdl);
}
super.generate(writer, alterColumn);
if (manageSystemVersioning) {
// make same changes to the history table
String historyTable = historyTable(tableName);
if (hasValue(alterColumn.getType()) || hasValue(alterColumn.getDefaultValue()) || alterColumn.isNotnull() != null) {
AlterColumn alterHistoryColumn = new AlterColumn();
alterHistoryColumn.setTableName(historyTable);
alterHistoryColumn.setColumnName(alterColumn.getColumnName());
alterHistoryColumn.setType(alterColumn.getType());
alterHistoryColumn.setDefaultValue(alterColumn.getDefaultValue());
alterHistoryColumn.setNotnull(alterColumn.isNotnull());
alterHistoryColumn.setCurrentType(alterColumn.getCurrentType());
alterHistoryColumn.setCurrentDefaultValue(alterColumn.getCurrentDefaultValue());
alterHistoryColumn.setCurrentNotnull(alterColumn.isCurrentNotnull());
String histColumnDdl = platformDdl.alterColumnBaseAttributes(alterHistoryColumn);
// write the apply to history table
writer.apply().append(histColumnDdl).endOfStatement();
}
historyDdl.enableSystemVersioning(writer.apply(), tableName, historyTable, false, this.generateUniqueDdl);
}
}
@Override
public void generate(DdlWrite writer, DropColumn dropColumn) {
String tableName = dropColumn.getTableName();
MTable table = writer.getTable(tableName);
if (table == null) {
super.generate(writer, dropColumn);
return;
}
boolean manageSystemVersioning = isTrue(table.isWithHistory()) && historyDdl.isSystemVersioningEnabled(tableName);
if (manageSystemVersioning) {
historyDdl.disableSystemVersioning(writer.apply(), tableName, this.generateUniqueDdl);
}
super.generate(writer, dropColumn);
if (manageSystemVersioning) {
// also drop from the history table
String historyTable = historyTable(tableName);
alterTableDropColumn(writer.apply(), historyTable, dropColumn.getColumnName());
historyDdl.enableSystemVersioning(writer.apply(), tableName, historyTable, false, this.generateUniqueDdl);
}
}
}
@@ -1,79 +0,0 @@
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
import java.util.ArrayList;
import java.util.List;
/**
* Bean holding comments relating to a history table that needs to have it's
* associated triggers/function updated due to columns added/dropped/included
* or excluded.
*/
public class HistoryTableUpdate {
/**
* Column change type.
*/
public enum Change {
ADD,
DROP,
INCLUDE,
EXCLUDE,
ALTER
}
private static class Column {
final Change change;
final String column;
public Column(Change change, String column) {
this.change = change;
this.column = column;
}
@Override
public String toString() {
return description();
}
public String description() {
return change.name().toLowerCase() + " " + column;
}
}
private final String baseTable;
private final List<Column> columnChanges = new ArrayList<>();
/**
* Construct with a given base table name.
*/
public HistoryTableUpdate(String baseTable) {
this.baseTable = baseTable;
}
/**
* Return a description of the changes that cause the history trigger/function
* to be regenerated (added, included, excluded and dropped columns).
*/
public String description() {
return columnChanges.toString();
}
/**
* Add a comment for column added, dropped, included or excluded.
*/
public void add(Change change, String column) {
columnChanges.add(new Column(change, column));
}
/**
* Return the base table name.
*/
public String getBaseTable() {
return baseTable;
}
}
@@ -1,6 +1,7 @@
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
import io.ebean.config.DatabaseConfig;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlAlterTable;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite;
import io.ebeaninternal.dbmigration.migration.AddHistoryTable;
@@ -12,9 +13,11 @@ import io.ebeaninternal.dbmigration.model.MTable;
*/
public class MariaDbHistoryDdl implements PlatformHistoryDdl {
private PlatformDdl platformDdl;
@Override
public void configure(DatabaseConfig config, PlatformDdl platformDdl) {
// do nothing
this.platformDdl = platformDdl;
}
@Override
@@ -24,8 +27,7 @@ public class MariaDbHistoryDdl implements PlatformHistoryDdl {
}
private void enableSystemVersioning(DdlWrite writer, String baseTable) {
DdlBuffer apply = writer.applyHistoryView();
apply.append("alter table ").append(baseTable).append(" add system versioning").endOfStatement();
platformDdl.alterTable(writer, baseTable).append("add system versioning", null);
DdlBuffer drop = writer.dropAll();
drop.append("alter table ").append(baseTable).append(" drop system versioning").endOfStatement();
@@ -34,8 +36,7 @@ public class MariaDbHistoryDdl implements PlatformHistoryDdl {
@Override
public void dropHistoryTable(DdlWrite writer, DropHistoryTable dropHistoryTable) {
String baseTable = dropHistoryTable.getBaseTable();
DdlBuffer apply = writer.applyHistoryView();
apply.append("alter table ").append(baseTable).append(" drop system versioning").endOfStatement();
platformDdl.alterTable(writer, baseTable).append("drop system versioning", null);
}
@Override
@@ -45,7 +46,14 @@ public class MariaDbHistoryDdl implements PlatformHistoryDdl {
}
@Override
public void updateTriggers(DdlWrite writer, HistoryTableUpdate baseTable) {
// do nothing
public void updateTriggers(DdlWrite writer, String tableName) {
MTable table = writer.getTable(tableName);
// For MariaDB we need to enable system_versioning_alter_history only once
// per DDL script. This info is stored in the virtual "__$HISTORY_FLAG__" table
DdlAlterTable history = platformDdl.alterTable(writer, "__$HISTORY_FLAG__");
if (table != null && table.isWithHistory() && !history.isHistoryHandled()) {
writer.apply().appendStatement("SET @@system_versioning_alter_history = 1");
history.setHistoryHandled();
}
}
}
@@ -3,11 +3,10 @@ package io.ebeaninternal.dbmigration.ddlgeneration.platform;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.util.StringHelper;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite;
import io.ebeaninternal.dbmigration.migration.AlterColumn;
import io.ebeaninternal.dbmigration.migration.Column;
import java.util.Collection;
/**
* MySql specific DDL.
*/
@@ -21,7 +20,7 @@ public class MySqlDdl extends PlatformDdl {
public MySqlDdl(DatabasePlatform platform) {
super(platform);
this.alterColumn = "modify";
this.alterColumn = "alter";
this.dropUniqueConstraint = "drop index";
this.historyDdl = new MySqlHistoryDdl();
this.inlineComments = true;
@@ -37,11 +36,11 @@ public class MySqlDdl extends PlatformDdl {
}
@Override
public void alterTableDropColumn(final DdlBuffer buffer, final String tableName, final String columnName) {
public void alterTableDropColumn(DdlWrite writer, String tableName, String columnName) {
if (this.useMigrationStoredProcedures) {
buffer.append("CALL usp_ebean_drop_column('").append(tableName).append("', '").append(columnName).append("')").endOfStatement();
alterTable(writer, tableName).raw("CALL usp_ebean_drop_column('").append(tableName).append("', '").append(columnName).append("')");
} else {
super.alterTableDropColumn(buffer, tableName, columnName);
super.alterTableDropColumn(writer, tableName, columnName);
}
}
@@ -91,38 +90,31 @@ public class MySqlDdl extends PlatformDdl {
}
@Override
public String alterColumnType(String tableName, String columnName, String type) {
// can't alter itself - done in alterColumnBaseAttributes()
return null;
}
@Override
public String alterColumnNotnull(String tableName, String columnName, boolean notnull) {
// can't alter itself - done in alterColumnBaseAttributes()
return null;
}
@Override
public String alterColumnDefaultValue(String tableName, String columnName, String defaultValue) {
String suffix = DdlHelp.isDropDefault(defaultValue) ? columnDropDefault : columnSetDefault + " " + convertDefaultValue(defaultValue);
return "alter table " + tableName + " alter " + columnName + " " + suffix;
}
@Override
public String alterColumnBaseAttributes(AlterColumn alter) {
if (alter.getType() == null && alter.isNotnull() == null) {
// No type change or notNull change
// defaultValue change already handled in alterColumnDefaultValue
return null;
}
public void alterColumn(DdlWrite writer, AlterColumn alter) {
String tableName = alter.getTableName();
String columnName = alter.getColumnName();
String type = alter.getType() != null ? alter.getType() : alter.getCurrentType();
type = convert(type);
boolean notnull = (alter.isNotnull() != null) ? alter.isNotnull() : Boolean.TRUE.equals(alter.isCurrentNotnull());
String notnullClause = notnull ? " not null" : "";
return "alter table " + tableName + " modify " + columnName + " " + type + notnullClause;
if (alter.getType() == null && alter.isNotnull() == null) {
// No type change or notNull change -> handle default value change
if (hasValue(alter.getDefaultValue())) {
alterColumnDefault(writer, alter);
}
} else {
// we must regenerate whole statement -> read altered and current value
String type = alter.getType() != null ? alter.getType() : alter.getCurrentType();
type = convert(type);
boolean notnull = (alter.isNotnull() != null) ? alter.isNotnull() : Boolean.TRUE.equals(alter.isCurrentNotnull());
String defaultValue = alter.getDefaultValue() != null ? alter.getDefaultValue() : alter.getCurrentDefaultValue();
DdlBuffer buffer = alterTable(writer, tableName).append("modify", columnName);
buffer.append(type);
if (notnull) {
buffer.append(" not null");
}
if (hasValue(defaultValue) && !DdlHelp.isDropDefault(defaultValue)) {
buffer.append(" default ").append(convertDefaultValue(defaultValue));
}
}
}
@Override
@@ -162,32 +154,4 @@ public class MySqlDdl extends PlatformDdl {
// alter comment currently not supported as it requires to repeat whole column definition
}
/**
* Locks all tables for triggers that have to be updated.
*/
@Override
public void lockTables(DdlBuffer buffer, Collection<String> tables) {
if (!tables.isEmpty()) {
buffer.append("lock tables ");
int i = 0;
for (String table : tables) {
if (i > 0) {
buffer.append(", ");
}
buffer.append(table).append(" write");
i++;
}
buffer.endOfStatement();
}
}
/**
* Unlocks all tables for triggers that have to be updated.
*/
@Override
public void unlockTables(DdlBuffer buffer, Collection<String> tables) {
buffer.append("unlock tables").endOfStatement();
}
}
@@ -1,8 +1,8 @@
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
import java.util.List;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite;
import io.ebeaninternal.dbmigration.model.MTable;
/**
* MySql history support using DB triggers to maintain a history table.
@@ -18,49 +18,36 @@ public class MySqlHistoryDdl extends DbTriggerBasedHistoryDdl {
buffer.append("drop trigger ").append(deleteTriggerName(baseTable)).endOfStatement();
}
@Override
protected void createTriggers(DdlWrite writer, MTable table) {
DbTriggerUpdate update = createDbTriggerUpdate(writer, table);
addBeforeUpdate(updateTriggerName(update.getBaseTable()), update);
addBeforeDelete(deleteTriggerName(update.getBaseTable()), update);
}
@Override
protected void updateHistoryTriggers(DbTriggerUpdate update) {
protected void createTriggers(DdlBuffer buffer, String baseTable, List<String> columnNames) {
recreateHistoryView(update);
buffer.append("lock tables ").append(baseTable).append(" write").endOfStatement();
addBeforeUpdate(buffer, updateTriggerName(baseTable), baseTable, columnNames);
addBeforeDelete(buffer, deleteTriggerName(baseTable), baseTable, columnNames);
buffer.appendStatement("unlock tables");
DdlBuffer buffer = update.historyTriggerBuffer();
String baseTable = update.getBaseTable();
dropTriggers(buffer, baseTable);
addBeforeUpdate(updateTriggerName(baseTable), update);
addBeforeDelete(deleteTriggerName(baseTable), update);
}
private void addBeforeUpdate(String triggerName, DbTriggerUpdate update) {
private void addBeforeUpdate(DdlBuffer apply, String triggerName, String tableName, List<String> columnNames) {
DdlBuffer apply = update.historyTriggerBuffer();
apply
.append("delimiter $$").newLine()
.append("create trigger ").append(triggerName).append(" before update on ").append(update.getBaseTable())
.append("create trigger ").append(triggerName).append(" before update on ").append(tableName)
.append(" for each row begin").newLine();
appendInsertIntoHistory(apply, update.getHistoryTable(), update.getColumns());
appendInsertIntoHistory(apply, tableName, columnNames);
apply
.append(" set NEW.").append(sysPeriod).append("_start = now(6)").endOfStatement()
.append("end$$").newLine();
}
private void addBeforeDelete(String triggerName, DbTriggerUpdate update) {
private void addBeforeDelete(DdlBuffer apply, String triggerName, String tableName, List<String> columnNames) {
DdlBuffer apply = update.historyTriggerBuffer();
apply
.append("delimiter $$").newLine()
.append("create trigger ").append(triggerName).append(" before delete on ").append(update.getBaseTable())
.append("create trigger ").append(triggerName).append(" before delete on ").append(tableName)
.append(" for each row begin").newLine();
appendInsertIntoHistory(apply, update.getHistoryTable(), update.getColumns());
appendInsertIntoHistory(apply, tableName, columnNames);
apply.append("end$$").newLine();
}
@@ -32,8 +32,4 @@ public class NoHistorySupportDdl implements PlatformHistoryDdl {
// does nothing
}
@Override
public void updateTriggers(DdlWrite writer, HistoryTableUpdate update) {
// does nothing
}
}
@@ -1,8 +1,9 @@
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
import java.util.List;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite;
import io.ebeaninternal.dbmigration.model.MTable;
/**
* NuoDB history support using DB triggers to maintain a history table.
@@ -22,48 +23,32 @@ public class NuoDbHistoryDdl extends DbTriggerBasedHistoryDdl {
}
@Override
protected void createTriggers(DdlWrite writer, MTable table) {
protected void createTriggers(DdlBuffer buffer, String baseTable, List<String> columnNames) {
DbTriggerUpdate update = createDbTriggerUpdate(writer, table);
addBeforeUpdate(updateTriggerName(update.getBaseTable()), update);
addBeforeDelete(deleteTriggerName(update.getBaseTable()), update);
addBeforeUpdate(buffer, updateTriggerName(baseTable), baseTable, columnNames);
addBeforeDelete(buffer, deleteTriggerName(baseTable), baseTable, columnNames);
}
@Override
protected void updateHistoryTriggers(DbTriggerUpdate update) {
private void addBeforeUpdate(DdlBuffer apply, String triggerName, String tableName, List<String> columnNames) {
recreateHistoryView(update);
addTriggerStart(triggerName, tableName, apply, " before update for each row as ");
DdlBuffer buffer = update.historyTriggerBuffer();
String baseTable = update.getBaseTable();
dropTriggers(buffer, baseTable);
addBeforeUpdate(updateTriggerName(baseTable), update);
addBeforeDelete(deleteTriggerName(baseTable), update);
}
private void addBeforeUpdate(String triggerName, DbTriggerUpdate update) {
DdlBuffer apply = update.historyTriggerBuffer();
addTriggerStart(triggerName, update, apply, " before update for each row as ");
apply.append(" NEW.sys_period_start = greatest(current_timestamp, date_add(OLD.sys_period_start, interval 1 microsecond))").endOfStatement();
appendInsertIntoHistory(apply, update.getHistoryTable(), update.getColumns());
appendInsertIntoHistory(apply, tableName, columnNames);
addEndTrigger(apply);
}
private void addBeforeDelete(String triggerName, DbTriggerUpdate update) {
private void addBeforeDelete(DdlBuffer apply, String triggerName, String tableName, List<String> columnNames) {
DdlBuffer apply = update.historyTriggerBuffer();
addTriggerStart(triggerName, update, apply, " before delete for each row as");
appendInsertIntoHistory(apply, update.getHistoryTable(), update.getColumns());
addTriggerStart(triggerName, tableName, apply, " before delete for each row as");
appendInsertIntoHistory(apply, tableName, columnNames);
addEndTrigger(apply);
}
private void addTriggerStart(String triggerName, DbTriggerUpdate update, DdlBuffer apply, String s) {
private void addTriggerStart(String triggerName, String baseTable, DdlBuffer apply, String s) {
apply
.append("delimiter $$").newLine()
.append("create or replace trigger ").append(triggerName).append(" for ").append(update.getBaseTable())
.append("create or replace trigger ").append(triggerName).append(" for ").append(baseTable)
.append(s).newLine();
}
@@ -18,7 +18,6 @@ import io.ebeaninternal.dbmigration.migration.Column;
import io.ebeaninternal.dbmigration.migration.DropHistoryTable;
import io.ebeaninternal.dbmigration.model.MTable;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
@@ -77,16 +76,12 @@ public class PlatformDdl {
protected String alterColumn = "alter column";
protected String alterColumnSuffix = "";
protected String dropUniqueConstraint = "drop constraint";
protected String addConstraint = "add constraint";
protected String addColumn = "add column";
protected String addColumnSuffix = "";
protected String columnSetType = "";
protected String columnSetDefault = "set default";
@@ -97,14 +92,14 @@ public class PlatformDdl {
protected String columnSetNull = "set null";
protected String columnNotNull = "not null";
protected String updateNullWithDefault = "update ${table} set ${column} = ${default} where ${column} is null";
protected String createTable = "create table";
protected String dropColumn = "drop column";
protected String dropColumnSuffix = "";
protected String addForeignKeySkipCheck = "";
protected String uniqueIndex = "unique";
@@ -272,20 +267,13 @@ public class PlatformDdl {
}
}
if (isTrue(column.isNotnull()) || isTrue(column.isPrimaryKey())) {
writeColumnNotNull(buffer);
buffer.appendWithSpace(columnNotNull);
}
// add check constraints later as we really want to give them a nice name
// so that the database can potentially provide a nice SQL error
}
/**
* Allow for platform overriding (e.g. ClickHouse).
*/
protected void writeColumnNotNull(DdlBuffer buffer) {
buffer.append(" not null");
}
/**
* Returns the check constraint.
*/
@@ -375,8 +363,8 @@ public class PlatformDdl {
/**
* Regenerate the history triggers (or function) due to a column being added/dropped/excluded or included.
*/
public void regenerateHistoryTriggers(DdlWrite writer, HistoryTableUpdate update) {
historyDdl.updateTriggers(writer, update);
public void regenerateHistoryTriggers(DdlWrite writer, String tableName) {
historyDdl.updateTriggers(writer, tableName);
}
/**
@@ -538,13 +526,11 @@ public class PlatformDdl {
return buffer.toString();
}
public void alterTableAddColumn(DdlBuffer buffer, String tableName, Column column, boolean onHistoryTable, String defaultValue) {
public void alterTableAddColumn(DdlWrite writer, String tableName, Column column, boolean onHistoryTable, String defaultValue) {
String convertedType = convert(column.getType());
buffer.append("alter table ").append(tableName)
.append(" ").append(addColumn).append(" ").append(column.getName())
.append(" ").append(convertedType);
DdlBuffer buffer = alterTable(writer, tableName).append(addColumn, column.getName());
buffer.append(convertedType);
// Add default value also to history table if it is not excluded
if (defaultValue != null) {
@@ -556,28 +542,35 @@ public class PlatformDdl {
if (!onHistoryTable) {
if (isTrue(column.isNotnull())) {
writeColumnNotNull(buffer);
buffer.appendWithSpace(columnNotNull);
}
buffer.append(addColumnSuffix);
buffer.endOfStatement();
// check constraints cannot be added in one statement for h2
if (!StringHelper.isNull(column.getCheckConstraint())) {
String ddl = alterTableAddCheckConstraint(tableName, column.getCheckConstraintName(), column.getCheckConstraint());
if (hasValue(ddl)) {
buffer.append(ddl).endOfStatement();
}
writer.applyPostAlter().appendStatement(ddl);
}
} else {
buffer.append(addColumnSuffix);
buffer.endOfStatement();
}
}
public void alterTableDropColumn(DdlBuffer buffer, String tableName, String columnName) {
buffer.append("alter table ").append(tableName).append(" ").append(dropColumn).append(" ").append(columnName)
.append(dropColumnSuffix).endOfStatement();
/**
* This method is used from DbTriggerBasedHistoryDdl to add the sysPeriodColumns.
*/
public void alterTableAddColumn(DdlWrite writer, String tableName, String columnName, String columnType, String defaultValue) {
String convertedType = convert(columnType);
DdlBuffer buffer = alterTable(writer, tableName).append(addColumn, columnName);
buffer.append(convertedType);
if (defaultValue != null) {
buffer.append(" default ");
buffer.append(defaultValue);
}
}
public void alterTableDropColumn(DdlWrite writer, String tableName, String columnName) {
alterTable(writer, tableName).append(dropColumn, columnName);
}
/**
@@ -592,22 +585,27 @@ public class PlatformDdl {
/**
* Alter a column type.
* <p>
* Note that that MySql and SQL Server instead use alterColumnBaseAttributes()
* Note that that MySql, SQL Server, and HANA instead use alterColumn()
* </p>
*/
public String alterColumnType(String tableName, String columnName, String type) {
return "alter table " + tableName + " " + alterColumn + " " + columnName + " " + columnSetType + convert(type) + alterColumnSuffix;
protected void alterColumnType(DdlWrite writer, AlterColumn alter) {
alterTable(writer, alter.getTableName()).append(alterColumn, alter.getColumnName())
.append(columnSetType).append(convert(alter.getType()));
}
/**
* Alter a column adding or removing the not null constraint.
* <p>
* Note that that MySql, SQL Server, and HANA instead use alterColumnBaseAttributes()
* Note that that MySql, SQL Server, and HANA instead use alterColumn()
* </p>
*/
public String alterColumnNotnull(String tableName, String columnName, boolean notnull) {
String suffix = notnull ? columnSetNotnull : columnSetNull;
return "alter table " + tableName + " " + alterColumn + " " + columnName + " " + suffix + alterColumnSuffix;
protected void alterColumnNotnull(DdlWrite writer, AlterColumn alter) {
DdlBuffer buffer = alterTable(writer, alter.getTableName()).append(alterColumn, alter.getColumnName());
if (Boolean.TRUE.equals(alter.isNotnull())) {
buffer.append(columnSetNotnull);
} else {
buffer.append(columnSetNull);
}
}
/**
@@ -619,22 +617,46 @@ public class PlatformDdl {
/**
* Alter column setting the default value.
* <p>
* Note that that MySql, SQL Server, and HANA instead use alterColumn()
* </p>
*/
public String alterColumnDefaultValue(String tableName, String columnName, String defaultValue) {
String suffix = DdlHelp.isDropDefault(defaultValue) ? columnDropDefault : columnSetDefault + " " + convertDefaultValue(defaultValue);
return "alter table " + tableName + " " + alterColumn + " " + columnName + " " + suffix + alterColumnSuffix;
protected void alterColumnDefault(DdlWrite writer, AlterColumn alter) {
DdlBuffer buffer = alterTable(writer, alter.getTableName()).append(alterColumn, alter.getColumnName());
if (DdlHelp.isDropDefault(alter.getDefaultValue())) {
buffer.append(columnDropDefault);
} else {
buffer.append(columnSetDefault).appendWithSpace(convertDefaultValue(alter.getDefaultValue()));
}
}
/**
* Alter column setting both the type and not null constraint.
* Alter column setting (type, default value and not null constraint).
* <p>
* Used by MySql, SQL Server, and HANA as these require both column attributes to be set together.
* Used by MySql, SQL Server, and HANA as these require all column attributes to
* be set together.
* </p>
*/
public String alterColumnBaseAttributes(AlterColumn alter) {
// by default do nothing, only used by mysql, sql server, and HANA as they can only
// modify the column with the full column definition
return null;
public void alterColumn(DdlWrite writer, AlterColumn alter) {
if (hasValue(alter.getType())) {
alterColumnType(writer, alter);
}
if (hasValue(alter.getDefaultValue())) {
alterColumnDefault(writer, alter);
}
if (alter.isNotnull() != null) {
alterColumnNotnull(writer, alter);
}
}
/**
* Creates or replace a new DdlAlterTable for given tableName.
*/
protected DdlAlterTable alterTable(DdlWrite writer, String tableName) {
return writer.applyAlterTable(tableName, BaseAlterTableWrite::new);
}
protected void appendColumns(String[] columns, StringBuilder buffer) {
@@ -757,20 +779,6 @@ public class PlatformDdl {
return name;
}
/**
* Mysql-specific: Locks all tables for triggers that have to be updated.
*/
public void lockTables(DdlBuffer buffer, Collection<String> tables) {
}
/**
* Mysql-specific: Unlocks all tables for triggers that have to be updated.
*/
public void unlockTables(DdlBuffer buffer, Collection<String> tables) {
}
/**
* Returns the database-specific "create table" command prefix. For HANA this is
* either "create column table" or "create row table", for all other databases
@@ -32,7 +32,19 @@ public interface PlatformHistoryDdl {
void addHistoryTable(DdlWrite writer, AddHistoryTable addHistoryTable);
/**
* Regenerate the history triggers/stored function due to column added/dropped/included or excluded.
* Returns true, if alters on the live tables should be applied also to the history tables. This is required for DbTriggerBased
* histories or on platforms like Hana, which are not SQL2011 history compatible (at least from DDL perspective)
*/
void updateTriggers(DdlWrite writer, HistoryTableUpdate baseTable);
default boolean alterHistoryTables() {
return false;
}
/**
* Regenerate the history triggers/stored function due to column added/dropped/included or excluded.
*
* Note: This function may be called multiple times for the same table.
*/
default void updateTriggers(DdlWrite writer, String tableName) {
// nop
}
}
@@ -2,6 +2,8 @@ package io.ebeaninternal.dbmigration.ddlgeneration.platform;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite;
import io.ebeaninternal.dbmigration.migration.AlterColumn;
/**
* Postgres specific DDL.
@@ -56,7 +58,10 @@ public class PostgresDdl extends PlatformDdl {
}
@Override
public String alterColumnType(String tableName, String columnName, String type) {
return super.alterColumnType(tableName, columnName, type) + " using " + columnName + "::" + convert(type);
protected void alterColumnType(DdlWrite writer, AlterColumn alter) {
String type = convert(alter.getType());
alterTable(writer, alter.getTableName()).append(alterColumn, alter.getColumnName())
.append(columnSetType).append(type)
.append(" using ").append(alter.getColumnName()).append("::").append(type);
}
}
@@ -29,14 +29,11 @@ public class PostgresHistoryDdl extends DbTriggerBasedHistoryDdl {
* Use Postgres range type rather than start and end timestamps.
*/
@Override
protected void addSysPeriodColumns(DdlBuffer apply, String baseTableName, String whenCreatedColumn) {
apply
.append("alter table ").append(baseTableName)
.append(" add column ").append(sysPeriod).append(" tstzrange not null default tstzrange(").append(now).append(", null)")
.endOfStatement();
protected void addSysPeriodColumns(DdlWrite writer, String baseTableName, String whenCreatedColumn) {
platformDdl.alterTableAddColumn(writer, baseTableName, sysPeriod, "tstzrange not null", "tstzrange(" + now + ", null)");
if (whenCreatedColumn != null) {
apply.append("update ").append(baseTableName).append(" set ")
writer.applyPostAlter()
.append("update ").append(baseTableName).append(" set ")
.append(sysPeriod).append(" = tstzrange(").append(whenCreatedColumn).append(", null)").endOfStatement();
}
}
@@ -47,18 +44,16 @@ public class PostgresHistoryDdl extends DbTriggerBasedHistoryDdl {
}
@Override
protected void dropSysPeriodColumns(DdlBuffer buffer, String baseTableName) {
buffer.append("alter table ").append(baseTableName).append(" drop column ").append(sysPeriod).endOfStatement();
protected void dropSysPeriodColumns(DdlWrite writer, String baseTableName) {
platformDdl.alterTableDropColumn(writer, baseTableName, sysPeriod);
}
@Override
protected void createTriggers(DdlWrite writer, MTable table) {
String baseTableName = table.getName();
protected void createTriggers(DdlBuffer buffer, String baseTableName, List<String> columnNames) {
String procedureName = procedureName(baseTableName);
String triggerName = triggerName(baseTableName);
DdlBuffer apply = writer.applyHistoryTrigger();
apply
createOrReplaceFunction(buffer, procedureName, historyTableName(baseTableName), columnNames);
buffer
.append("create trigger ").append(triggerName).newLine()
.append(" before update or delete on ").append(baseTableName).newLine()
.append(" for each row execute procedure ").append(procedureName).append("();").newLine().newLine();
@@ -103,22 +98,6 @@ public class PostgresHistoryDdl extends DbTriggerBasedHistoryDdl {
apply.end();
}
@Override
protected void createStoredFunction(DdlWrite writer, MTable table) {
String procedureName = procedureName(table.getName());
String historyTable = historyTableName(table.getName());
List<String> columnNames = columnNamesForApply(table);
createOrReplaceFunction(writer.applyHistoryTrigger(), procedureName, historyTable, columnNames);
}
@Override
protected void updateHistoryTriggers(DbTriggerUpdate update) {
String procedureName = procedureName(update.getBaseTable());
recreateHistoryView(update);
createOrReplaceFunction(update.historyTriggerBuffer(), procedureName, update.getHistoryTable(), update.getColumns());
}
@Override
protected void appendInsertIntoHistory(DdlBuffer buffer, String historyTable, List<String> columns) {
buffer.append(" insert into ").append(historyTable).append(" (").append(sysPeriod).append(",");
@@ -1,8 +1,13 @@
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
import java.util.ArrayList;
import java.util.List;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlAlterTable;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlOptions;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite;
/**
* SQLite platform specific DDL.
@@ -59,18 +64,28 @@ public class SQLiteDdl extends PlatformDdl {
}
@Override
public String alterColumnDefaultValue(String tableName, String columnName, String defaultValue) {
return "-- not supported: " + super.alterColumnDefaultValue(tableName, columnName, defaultValue);
protected DdlAlterTable alterTable(DdlWrite writer, String tableName) {
return writer.applyAlterTable(tableName, SQLiteAlterTableWrite::new);
}
@Override
public String alterColumnType(String tableName, String columnName, String type) {
return "-- not supported: " + super.alterColumnType(tableName, columnName, type);
}
static class SQLiteAlterTableWrite extends BaseAlterTableWrite {
public SQLiteAlterTableWrite(String tableName) {
super(tableName);
}
@Override
public String alterColumnNotnull(String tableName, String columnName, boolean notnull) {
return "-- not supported: " + super.alterColumnNotnull(tableName, columnName, notnull);
@Override
protected List<AlterCmd> postProcessCommands(List<AlterCmd> cmds) {
List<AlterCmd> ret = new ArrayList<AlterCmd>();
for (AlterCmd cmd : cmds) {
switch (cmd.getOperation()) {
case "alter column":
ret.add(newRawCommand("-- not supported: " + cmd));
break;
default:
ret.add(cmd);
}
}
return ret;
}
}
}
@@ -11,6 +11,12 @@ import io.ebeaninternal.dbmigration.migration.AlterColumn;
*/
public class SqlServerDdl extends PlatformDdl {
private static final String CONSTRAINT = "C";
private static final String UNIQUE_CONSTRAINT = "UQ";
private static final String USER_TABLE = "U";
private static final String FOREIGN_KEY = "F";
private static final String SEQUENCE_OBJECT = "SO";
public SqlServerDdl(DatabasePlatform platform) {
super(platform);
this.identitySuffix = " identity(1,1)";
@@ -31,12 +37,7 @@ public class SqlServerDdl extends PlatformDdl {
@Override
public String dropTable(String tableName) {
StringBuilder buffer = new StringBuilder();
buffer.append("IF OBJECT_ID('");
buffer.append(tableName);
buffer.append("', 'U') IS NOT NULL drop table ");
buffer.append(tableName);
return buffer.toString();
return ifObjectExists(tableName, USER_TABLE) + "drop table " + tableName;
}
@Override
@@ -46,12 +47,12 @@ public class SqlServerDdl extends PlatformDdl {
if (pos != -1) {
objectId = tableName.substring(0, pos + 1) + fkName;
}
return "IF OBJECT_ID('" + objectId + "', 'F') IS NOT NULL " + super.alterTableDropForeignKey(tableName, fkName);
return ifObjectExists(objectId, FOREIGN_KEY) + super.alterTableDropForeignKey(tableName, fkName);
}
@Override
public String dropSequence(String sequenceName) {
return "IF OBJECT_ID('" + sequenceName + "', 'SO') IS NOT NULL drop sequence " + sequenceName;
return ifObjectExists(sequenceName, SEQUENCE_OBJECT) + "drop sequence " + sequenceName;
}
@Override
@@ -71,12 +72,11 @@ public class SqlServerDdl extends PlatformDdl {
throw new NullPointerException();
}
// issues#233
String start = "create unique nonclustered index " + uqName + " on " + tableName + "(";
StringBuilder sb = new StringBuilder(start);
StringBuilder sb = new StringBuilder(256);
sb.append("create unique nonclustered index ").append(uqName).append(" on ").append(tableName).append('(');
for (int i = 0; i < columns.length; i++) {
if (i > 0) {
sb.append(",");
sb.append(',');
}
sb.append(columns[i]);
}
@@ -91,11 +91,9 @@ public class SqlServerDdl extends PlatformDdl {
@Override
public String alterTableDropConstraint(String tableName, String constraintName) {
StringBuilder sb = new StringBuilder();
sb.append("IF (OBJECT_ID('").append(constraintName).append("', 'C') IS NOT NULL) ");
sb.append(super.alterTableDropConstraint(tableName, constraintName));
return sb.toString();
return ifObjectExists(maxConstraintName(constraintName), CONSTRAINT) + super.alterTableDropConstraint(tableName, constraintName);
}
/**
* Drop a unique constraint from the table (Sometimes this is an index).
*/
@@ -103,10 +101,12 @@ public class SqlServerDdl extends PlatformDdl {
public String alterTableDropUniqueConstraint(String tableName, String uniqueConstraintName) {
StringBuilder sb = new StringBuilder();
sb.append(dropIndex(uniqueConstraintName, tableName)).append(";\n");
sb.append("IF (OBJECT_ID('").append(maxConstraintName(uniqueConstraintName)).append("', 'UQ') IS NOT NULL) ");
sb.append(super.alterTableDropUniqueConstraint(tableName, uniqueConstraintName));
sb.append(ifObjectExists(maxConstraintName(uniqueConstraintName), UNIQUE_CONSTRAINT))
.append(super.alterTableDropUniqueConstraint(tableName, uniqueConstraintName));
return sb.toString();
}
/**
* Generate and return the create sequence DDL.
*/
@@ -133,50 +133,53 @@ public class SqlServerDdl extends PlatformDdl {
}
@Override
public String alterColumnDefaultValue(String tableName, String columnName, String defaultValue) {
protected void alterColumnDefault(DdlWrite writer, AlterColumn alter) {
// Unfortunately, the SqlServer creates default values with a random name.
// You can specify a name in DDL, but this does not work in conjunction with
// temporal tables in certain cases. So we have to delete the constraint with
// a rather complex statement.
StringBuilder sb = new StringBuilder();
if (DdlHelp.isDropDefault(defaultValue)) {
sb.append("EXEC usp_ebean_drop_default_constraint ").append(tableName).append(", ").append(columnName);
} else {
sb.append("alter table ").append(tableName);
sb.append(" add default ").append(convertDefaultValue(defaultValue)).append(" for ").append(columnName);
}
return sb.toString();
}
@Override
public String alterColumnBaseAttributes(AlterColumn alter) {
if (alter.getType() == null && alter.isNotnull() == null) {
// No type change or notNull change
// defaultValue change already handled in alterColumnDefaultValue
return null;
}
String tableName = alter.getTableName();
String columnName = alter.getColumnName();
String type = alter.getType() != null ? alter.getType() : alter.getCurrentType();
type = convert(type);
boolean notnull = (alter.isNotnull() != null) ? alter.isNotnull() : Boolean.TRUE.equals(alter.isCurrentNotnull());
String notnullClause = notnull ? " not null" : "";
return "alter table " + tableName + " " + alterColumn + " " + columnName + " " + type + notnullClause;
String defaultValue = alter.getDefaultValue();
if (DdlHelp.isDropDefault(defaultValue)) {
execUspDropDefaultConstraint(writer, tableName, columnName);
} else {
execUspDropDefaultConstraint(writer, tableName, columnName);
setDefaultValue(writer, tableName, columnName, defaultValue);
}
}
@Override
public String alterColumnType(String tableName, String columnName, String type) {
public void alterColumn(DdlWrite writer, AlterColumn alter) {
String tableName = alter.getTableName();
String columnName = alter.getColumnName();
if (alter.getType() == null && alter.isNotnull() == null) {
// No type change or notNull change
if (hasValue(alter.getDefaultValue())) {
alterColumnDefault(writer, alter);
}
} else {
// we must regenerate whole statement -> read altered and current value
String type = alter.getType() != null ? alter.getType() : alter.getCurrentType();
type = convert(type);
boolean notnull = (alter.isNotnull() != null) ? alter.isNotnull() : Boolean.TRUE.equals(alter.isCurrentNotnull());
String defaultValue = alter.getDefaultValue() != null ? alter.getDefaultValue() : alter.getCurrentDefaultValue();
if (hasValue(defaultValue)) {
// default value present -> drop default constraint before altering
execUspDropDefaultConstraint(writer, tableName, columnName);
}
// can't alter itself - done in alterColumnBaseAttributes()
return null;
}
DdlBuffer buffer = alterTable(writer, tableName).append(alterColumn, columnName);
buffer.append(type);
if (notnull) {
buffer.append(" not null");
}
@Override
public String alterColumnNotnull(String tableName, String columnName, boolean notnull) {
// can't alter itself - done in alterColumnBaseAttributes()
return null;
// re add - default constraint
if (hasValue(defaultValue) && !DdlHelp.isDropDefault(defaultValue)) {
setDefaultValue(writer, tableName, columnName, defaultValue);
}
}
}
/**
@@ -202,9 +205,8 @@ public class SqlServerDdl extends PlatformDdl {
* (constraints, default values, indices and foreign keys). That's why we call a user stored procedure here
*/
@Override
public void alterTableDropColumn(DdlBuffer buffer, String tableName, String columnName) {
buffer.append("EXEC usp_ebean_drop_column ").append(tableName).append(", ").append(columnName).endOfStatement();
public void alterTableDropColumn(DdlWrite writer, String tableName, String columnName) {
alterTable(writer, tableName).raw("EXEC usp_ebean_drop_column ").append(tableName).append(", ").append(columnName);
}
/**
@@ -246,4 +248,15 @@ public class SqlServerDdl extends PlatformDdl {
.endOfStatement();
}
public static String ifObjectExists(String object, String objectType) {
return "IF OBJECT_ID('" + object + "', '" + objectType + "') IS NOT NULL ";
}
private void execUspDropDefaultConstraint(DdlWrite writer, String tableName, String columnName) {
alterTable(writer, tableName).raw("EXEC usp_ebean_drop_default_constraint " + tableName + ", " + columnName);
}
private void setDefaultValue(DdlWrite writer, String tableName, String columnName, String defaultValue) {
alterTable(writer, tableName).append("add default " + convertDefaultValue(defaultValue) + " for", columnName);
}
}
@@ -1,9 +1,11 @@
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
import io.ebean.config.DatabaseConfig;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlAlterTable;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite;
import io.ebeaninternal.dbmigration.migration.AddHistoryTable;
import io.ebeaninternal.dbmigration.migration.AlterColumn;
import io.ebeaninternal.dbmigration.migration.DropHistoryTable;
import io.ebeaninternal.dbmigration.model.MTable;
@@ -42,7 +44,7 @@ public class SqlServerHistoryDdl implements PlatformHistoryDdl {
}
private void enableSystemVersioning(DdlWrite writer, String baseTable) {
DdlBuffer apply = writer.applyHistoryView();
DdlBuffer apply = writer.applyPostAlter();
apply.append("alter table ").append(baseTable).newLine()
.append(" add ").append(systemPeriodStart).append(" datetime2 GENERATED ALWAYS AS ROW START NOT NULL DEFAULT SYSUTCDATETIME(),").newLine()
.append(" ").append(systemPeriodEnd).append(" datetime2 GENERATED ALWAYS AS ROW END NOT NULL DEFAULT '9999-12-31T23:59:59.9999999',").newLine()
@@ -59,20 +61,25 @@ public class SqlServerHistoryDdl implements PlatformHistoryDdl {
@Override
public void dropHistoryTable(DdlWrite writer, DropHistoryTable dropHistoryTable) {
String baseTable = dropHistoryTable.getBaseTable();
DdlBuffer apply = writer.applyHistoryView();
apply.append("-- dropping history support for ").append(baseTable).endOfStatement();
// drop default constraints
AlterColumn alter = new AlterColumn();
alter.setTableName(baseTable);
alter.setDefaultValue(DdlHelp.DROP_DEFAULT);
alter.setColumnName(systemPeriodStart);
platformDdl.alterColumn(writer, alter);
alter.setColumnName(systemPeriodEnd);
platformDdl.alterColumn(writer, alter);
apply.append(platformDdl.alterColumnDefaultValue(baseTable, systemPeriodStart, DdlHelp.DROP_DEFAULT)).endOfStatement();
apply.append(platformDdl.alterColumnDefaultValue(baseTable, systemPeriodEnd, DdlHelp.DROP_DEFAULT)).endOfStatement();
// switch of versioning & period
// switch of versioning & period - must be done before altering
DdlBuffer apply = writer.apply();
apply.append("-- dropping history support for ").append(baseTable).endOfStatement();
apply.append("alter table ").append(baseTable).append(" set (system_versioning = off)").endOfStatement();
apply.append("alter table ").append(baseTable).append(" drop period for system_time").endOfStatement();
// now drop tables & columns
apply.append("alter table ").append(baseTable).append(" drop column ").append(systemPeriodStart).endOfStatement();
apply.append("alter table ").append(baseTable).append(" drop column ").append(systemPeriodEnd).endOfStatement();
apply.append("IF OBJECT_ID('").append(baseTable).append("_history', 'U') IS NOT NULL drop table ").append(baseTable).append("_history").endOfStatement();
apply.end();
// now drop tables & columns, they will go to alter table/post alter buffers
platformDdl.alterTableDropColumn(writer, baseTable, systemPeriodStart);
platformDdl.alterTableDropColumn(writer, baseTable, systemPeriodEnd);
writer.applyPostAlter().appendStatement(platformDdl.dropTable(baseTable + "_history"));
}
@Override
@@ -82,15 +89,19 @@ public class SqlServerHistoryDdl implements PlatformHistoryDdl {
}
@Override
public void updateTriggers(DdlWrite writer, HistoryTableUpdate baseTable) {
// SQL Server 2016 does not need triggers
DdlBuffer apply = writer.applyHistoryView();
String baseTableName = baseTable.getBaseTable();
apply.append("-- alter table ").append(baseTableName).append(" set (system_versioning = off (history_table=")
.append(getHistoryTable(baseTableName)).append("))").endOfStatement();
apply.append("-- history migration goes here").newLine();
apply.append("-- alter table ").append(baseTableName).append(" set (system_versioning = on (history_table=")
.append(getHistoryTable(baseTableName)).append("))").endOfStatement();
public void updateTriggers(DdlWrite writer, String tableName) {
DdlAlterTable alter = platformDdl.alterTable(writer, tableName);
writer.getTable(tableName);
if (!alter.isHistoryHandled()) {
// SQL Server 2016 does not need triggers
DdlBuffer apply = writer.apply();
apply.append("-- alter table ").append(tableName).append(" set (system_versioning = off (history_table=")
.append(getHistoryTable(tableName)).append("))").endOfStatement();
apply.append("-- history migration goes here").newLine();
apply.append("-- alter table ").append(tableName).append(" set (system_versioning = on (history_table=")
.append(getHistoryTable(tableName)).append("))").endOfStatement();
}
alter.setHistoryHandled();
}
}
@@ -16,6 +16,7 @@ import io.ebeaninternal.extraddl.model.ExtraDdl;
import io.ebeaninternal.extraddl.model.ExtraDdlXmlReader;
import io.ebeaninternal.dbmigration.ddlgeneration.PlatformDdlBuilder;
import java.io.IOException;
import java.util.List;
import static io.ebeaninternal.api.PlatformMatch.matchPlatform;
@@ -127,10 +128,13 @@ public class CurrentModel {
if (jaxbPresent) {
addExtraDdl(ddl, ExtraDdlXmlReader.readBuiltin(), "-- init script ");
}
ddl.append(writer.apply().getBuffer());
ddl.append(writer.applyForeignKeys().getBuffer());
ddl.append(writer.applyHistoryView().getBuffer());
ddl.append(writer.applyHistoryTrigger().getBuffer());
try {
writer.writeApply(ddl);
} catch (IOException e) { // should not happen on StringBuilder
throw new RuntimeException(e);
}
return ddl.toString();
}
@@ -157,8 +161,11 @@ public class CurrentModel {
if (ddlHeader != null && !ddlHeader.isEmpty()) {
ddl.append(ddlHeader).append('\n');
}
ddl.append(writer.dropAllForeignKeys().getBuffer());
ddl.append(writer.dropAll().getBuffer());
try {
writer.writeDropAll(ddl);
} catch (IOException e) { // should not happen on StringBuilder
throw new RuntimeException(e);
}
return ddl.toString();
}
@@ -3,7 +3,6 @@ package io.ebeaninternal.dbmigration.model;
import io.ebean.config.DatabaseConfig;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.util.IOUtils;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlHandler;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite;
import io.ebeaninternal.dbmigration.ddlgeneration.PlatformDdlBuilder;
@@ -91,21 +90,7 @@ public class PlatformDdlWriter {
if (header != null && !header.isEmpty()) {
writer.append(header).append('\n');
}
// merge the apply buffers in the appropriate order
prependDropDependencies(writer, ddl.applyDropDependencies());
writer.append("-- apply changes\n");
writer.append(ddl.apply().getBuffer());
writer.append(ddl.applyForeignKeys().getBuffer());
writer.append(ddl.applyHistoryView().getBuffer());
writer.append(ddl.applyHistoryTrigger().getBuffer());
}
private void prependDropDependencies(Writer writer, DdlBuffer buffer) throws IOException {
if (!buffer.isEmpty()) {
writer.append("-- drop dependencies\n");
writer.append(buffer.getBuffer());
writer.append("\n");
}
ddl.writeApply(writer);
}
/**
@@ -19,7 +19,7 @@ import static org.assertj.core.api.Assertions.assertThat;
public class BaseDdlHandlerTest extends BaseTestCase {
private static boolean useV1Syntax = Boolean.getBoolean("ebean.h2.useV1Syntax");
private final DatabaseConfig serverConfig = new DatabaseConfig();
private DdlHandler handler(DatabasePlatform platform) {
@@ -47,15 +47,15 @@ public class BaseDdlHandlerTest extends BaseTestCase {
DdlWrite writer = new DdlWrite();
h2Handler().generate(writer, Helper.getAddColumn());
assertThat(writer.apply().getBuffer()).isEqualTo("alter table foo add column added_to_foo varchar(20);\n\n");
assertThat(writer.toString()).isEqualTo("-- apply alter tables\nalter table foo add column added_to_foo varchar(20);\n");
writer = new DdlWrite();
sqlserverHandler().generate(writer, Helper.getAddColumn());
assertThat(writer.apply().getBuffer()).isEqualTo("alter table foo add added_to_foo nvarchar(20);\n\n");
assertThat(writer.toString()).isEqualTo("-- apply alter tables\nalter table foo add added_to_foo nvarchar(20);\n");
writer = new DdlWrite();
hanaHandler().generate(writer, Helper.getAddColumn());
assertThat(writer.apply().getBuffer()).isEqualTo("alter table foo add ( added_to_foo nvarchar(20));\n\n");
assertThat(writer.toString()).isEqualTo("-- apply alter tables\nalter table foo add (added_to_foo nvarchar(20));\n");
}
@Test
@@ -63,18 +63,17 @@ public class BaseDdlHandlerTest extends BaseTestCase {
DdlWrite writer = new DdlWrite();
h2Handler().generate(writer, Helper.getAlterTableAddColumnWithCheckConstraint());
assertThat(writer.apply().getBuffer()).isEqualTo("alter table foo add column status integer;\n"
+ "alter table foo add constraint ck_ordering_status check ( status in (0,1));\n\n");
assertThat(writer.toString()).isEqualTo("-- apply alter tables\nalter table foo add column status integer;\n"
+ "-- apply post alter\nalter table foo add constraint ck_ordering_status check ( status in (0,1));\n");
writer = new DdlWrite();
hanaHandler().generate(writer, Helper.getAlterTableAddColumnWithCheckConstraint());
assertThat(writer.apply().getBuffer()).isEqualTo("alter table foo add ( status integer);\n"
+ "alter table foo add constraint ck_ordering_status check ( status in (0,1));\n\n");
assertThat(writer.toString()).isEqualTo("-- apply alter tables\nalter table foo add (status integer);\n"
+ "-- apply post alter\nalter table foo add constraint ck_ordering_status check ( status in (0,1));\n");
}
/**
* Test the functionality of the Ebean {@literal @}DbArray extension during DDL
* generation.
* Test the functionality of the Ebean {@literal @}DbArray extension during DDL generation.
*/
@Test
public void addColumn_dbarray() throws Exception {
@@ -84,20 +83,20 @@ public class BaseDdlHandlerTest extends BaseTestCase {
DdlHandler postgresHandler = postgresHandler();
postgresHandler.generate(writer, Helper.getAlterTableAddDbArrayColumn());
assertThat(writer.apply().getBuffer()).isEqualTo("alter table foo add column dbarray_added_to_foo varchar[];\n\n");
assertThat(writer.toString()).isEqualTo("-- apply alter tables\nalter table foo add column dbarray_added_to_foo varchar[];\n");
writer = new DdlWrite();
DdlHandler sqlserverHandler = sqlserverHandler();
sqlserverHandler.generate(writer, Helper.getAlterTableAddDbArrayColumn());
assertThat(writer.apply().getBuffer()).isEqualTo("alter table foo add dbarray_added_to_foo varchar(1000);\n\n");
assertThat(writer.toString()).isEqualTo("-- apply alter tables\nalter table foo add dbarray_added_to_foo varchar(1000);\n");
writer = new DdlWrite();
DdlHandler hanaHandler = hanaHandler();
hanaHandler.generate(writer, Helper.getAlterTableAddDbArrayColumn());
assertThat(writer.apply().getBuffer())
.isEqualTo("alter table foo add ( dbarray_added_to_foo nvarchar(255) array);\n\n");
assertThat(writer.toString())
.isEqualTo("-- apply alter tables\nalter table foo add (dbarray_added_to_foo nvarchar(255) array);\n");
}
@Test
@@ -106,24 +105,24 @@ public class BaseDdlHandlerTest extends BaseTestCase {
DdlWrite writer = new DdlWrite();
postgresHandler().generate(writer, Helper.getAlterTableAddDbArrayColumnWithLength());
assertThat(writer.apply().getBuffer()).isEqualTo("alter table foo add column dbarray_ninety varchar[];\n\n");
assertThat(writer.toString()).isEqualTo("-- apply alter tables\nalter table foo add column dbarray_ninety varchar[];\n");
writer = new DdlWrite();
h2Handler().generate(writer, Helper.getAlterTableAddDbArrayColumnWithLength());
if (useV1Syntax) {
assertThat(writer.apply().getBuffer()).isEqualTo("alter table foo add column dbarray_ninety array;\n\n");
assertThat(writer.toString()).isEqualTo("-- apply alter tables\nalter table foo add column dbarray_ninety array;\n");
} else {
assertThat(writer.apply().getBuffer()).isEqualTo("alter table foo add column dbarray_ninety varchar array;\n\n");
assertThat(writer.toString()).isEqualTo("-- apply alter tables\nalter table foo add column dbarray_ninety varchar array;\n");
}
writer = new DdlWrite();
sqlserverHandler().generate(writer, Helper.getAlterTableAddDbArrayColumnWithLength());
assertThat(writer.apply().getBuffer()).isEqualTo("alter table foo add dbarray_ninety varchar(90);\n\n");
assertThat(writer.toString()).isEqualTo("-- apply alter tables\nalter table foo add dbarray_ninety varchar(90);\n");
writer = new DdlWrite();
hanaHandler().generate(writer, Helper.getAlterTableAddDbArrayColumnWithLength());
assertThat(writer.apply().getBuffer())
.isEqualTo("alter table foo add ( dbarray_ninety nvarchar(255) array(90));\n\n");
assertThat(writer.toString())
.isEqualTo("-- apply alter tables\nalter table foo add (dbarray_ninety nvarchar(255) array(90));\n");
}
@Test
@@ -131,31 +130,31 @@ public class BaseDdlHandlerTest extends BaseTestCase {
DdlWrite writer = new DdlWrite();
postgresHandler().generate(writer, Helper.getAlterTableAddDbArrayColumnIntegerWithLength());
assertThat(writer.apply().getBuffer()).isEqualTo("alter table foo add column dbarray_integer integer[];\n\n");
assertThat(writer.toString()).isEqualTo("-- apply alter tables\nalter table foo add column dbarray_integer integer[];\n");
writer = new DdlWrite();
h2Handler().generate(writer, Helper.getAlterTableAddDbArrayColumnIntegerWithLength());
if (useV1Syntax) {
assertThat(writer.apply().getBuffer()).isEqualTo("alter table foo add column dbarray_integer array;\n\n");
assertThat(writer.toString()).isEqualTo("-- apply alter tables\nalter table foo add column dbarray_integer array;\n");
} else {
assertThat(writer.apply().getBuffer()).isEqualTo("alter table foo add column dbarray_integer integer array;\n\n");
assertThat(writer.toString()).isEqualTo("-- apply alter tables\nalter table foo add column dbarray_integer integer array;\n");
}
writer = new DdlWrite();
sqlserverHandler().generate(writer, Helper.getAlterTableAddDbArrayColumnIntegerWithLength());
assertThat(writer.apply().getBuffer()).isEqualTo("alter table foo add dbarray_integer varchar(90);\n\n");
assertThat(writer.toString()).isEqualTo("-- apply alter tables\nalter table foo add dbarray_integer varchar(90);\n");
writer = new DdlWrite();
sqlserverHandler().generate(writer, Helper.getAlterTableAddDbArrayColumnInteger());
assertThat(writer.apply().getBuffer()).isEqualTo("alter table foo add dbarray_integer varchar(1000);\n\n");
assertThat(writer.toString()).isEqualTo("-- apply alter tables\nalter table foo add dbarray_integer varchar(1000);\n");
writer = new DdlWrite();
hanaHandler().generate(writer, Helper.getAlterTableAddDbArrayColumnIntegerWithLength());
assertThat(writer.apply().getBuffer()).isEqualTo("alter table foo add ( dbarray_integer integer array(90));\n\n");
assertThat(writer.toString()).isEqualTo("-- apply alter tables\nalter table foo add (dbarray_integer integer array(90));\n");
writer = new DdlWrite();
hanaHandler().generate(writer, Helper.getAlterTableAddDbArrayColumnInteger());
assertThat(writer.apply().getBuffer()).isEqualTo("alter table foo add ( dbarray_integer integer array);\n\n");
assertThat(writer.toString()).isEqualTo("-- apply alter tables\nalter table foo add (dbarray_integer integer array);\n");
}
@Test
@@ -166,12 +165,12 @@ public class BaseDdlHandlerTest extends BaseTestCase {
DdlHandler handler = h2Handler();
handler.generate(writer, Helper.getAlterTableAddColumn());
String buffer = writer.apply().getBuffer();
assertThat(buffer).contains("alter table foo add column some_id integer;");
String buffer = writer.toString();
assertThat(buffer).contains("-- apply alter tables\nalter table foo add column some_id integer;");
String fkBuffer = writer.applyForeignKeys().getBuffer();
assertThat(fkBuffer).contains(
"alter table foo add constraint fk_foo_some_id foreign key (some_id) references bar (id) on delete restrict on update restrict;");
"alter table foo add constraint fk_foo_some_id foreign key (some_id) references bar (id) on delete restrict on update restrict;");
assertThat(fkBuffer).contains("create index idx_foo_some_id on foo (some_id);");
assertThat(writer.dropAll().getBuffer()).isEqualTo("");
}
@@ -184,7 +183,7 @@ public class BaseDdlHandlerTest extends BaseTestCase {
handler.generate(writer, Helper.getDropColumn());
assertThat(writer.apply().getBuffer()).isEqualTo("alter table foo drop column col2;\n\n");
assertThat(writer.toString()).isEqualTo("-- apply alter tables\nalter table foo drop column col2;\n");
assertThat(writer.dropAll().getBuffer()).isEqualTo("");
writer = new DdlWrite();
@@ -192,7 +191,7 @@ public class BaseDdlHandlerTest extends BaseTestCase {
hanaHandler.generate(writer, Helper.getDropColumn());
assertThat(writer.apply().getBuffer()).isEqualTo("CALL usp_ebean_drop_column('foo', 'col2');\n\n");
assertThat(writer.toString()).isEqualTo("-- apply alter tables\nCALL usp_ebean_drop_column('foo', 'col2');\n");
assertThat(writer.dropAll().getBuffer()).isEqualTo("");
}
@@ -204,9 +203,9 @@ public class BaseDdlHandlerTest extends BaseTestCase {
handler.generate(writer, Helper.getCreateTable());
String createTableDDL = Helper.asText(this, "/assert/create-table.txt");
String createTableDDL = Helper.asText(this, "/assert/drop-create-table.txt");
assertThat(writer.apply().getBuffer()).isEqualTo(createTableDDL);
assertThat(writer.toString()).isEqualTo(createTableDDL);
assertThat(writer.dropAll().getBuffer().trim()).isEqualTo("drop table if exists foo;");
writer = new DdlWrite();
@@ -214,9 +213,9 @@ public class BaseDdlHandlerTest extends BaseTestCase {
hanaHandler.generate(writer, Helper.getCreateTable());
String createColumnTableDDL = Helper.asText(this, "/assert/create-column-table.txt");
String createColumnTableDDL = Helper.asText(this, "/assert/drop-create-column-table.txt");
assertThat(writer.apply().getBuffer()).isEqualTo(createColumnTableDDL);
assertThat(writer.toString()).isEqualTo(createColumnTableDDL);
assertThat(writer.dropAll().getBuffer().trim()).isEqualTo("drop table foo cascade;");
}
@@ -230,9 +229,12 @@ public class BaseDdlHandlerTest extends BaseTestCase {
String apply = Helper.asText(this, "/assert/BaseDdlHandlerTest/baseApply.sql");
String rollbackLast = Helper.asText(this, "/assert/BaseDdlHandlerTest/baseDropAll.sql");
assertThat(writer.apply().getBuffer()).isEqualTo(apply);
assertThat(writer.dropAll().getBuffer()).isEqualTo(rollbackLast);
StringBuilder sb = new StringBuilder();
writer.writeApply(sb);
assertThat(sb.toString()).isEqualTo(apply);
sb = new StringBuilder();
writer.writeDropAll(sb);
assertThat(sb.toString()).isEqualTo(rollbackLast);
}
@Disabled
@@ -251,7 +253,7 @@ public class BaseDdlHandlerTest extends BaseTestCase {
String apply = Helper.asText(this, "/assert/changeset-apply.txt");
String rollbackLast = Helper.asText(this, "/assert/changeset-dropAll.txt");
assertThat(writer.apply().getBuffer()).isEqualTo(apply);
assertThat(writer.toString()).isEqualTo(apply);
assertThat(writer.dropAll().getBuffer()).isEqualTo(rollbackLast);
}
@@ -272,7 +274,7 @@ public class BaseDdlHandlerTest extends BaseTestCase {
String rollbackFirst = Helper.asText(this, "/assert/changeset-pg-rollbackFirst.sql");
String rollbackLast = Helper.asText(this, "/assert/changeset-pg-rollbackLast.sql");
assertThat(writer.apply().getBuffer()).isEqualTo(apply);
assertThat(writer.toString()).isEqualTo(apply);
assertThat(writer.applyForeignKeys().getBuffer()).isEqualTo(applyLast);
assertThat(writer.dropAllForeignKeys().getBuffer()).isEqualTo(rollbackFirst);
assertThat(writer.dropAll().getBuffer()).isEqualTo(rollbackLast);
@@ -39,7 +39,7 @@ public class BaseTableDdlTest {
ddlGen.generate(writer, alterColumn);
String ddl = writer.apply().getBuffer();
String ddl = writer.toString();
assertThat(ddl).contains("alter table mytab drop constraint if exists ck_mytab_acol");
assertThat(ddl).contains("alter table mytab add constraint ck_mytab_acol check (acol in ('A','B'))");
}
@@ -55,9 +55,9 @@ public class BaseTableDdlTest {
column.setName("col_name");
column.setType("varchar(20)");
ddlGen.alterTableAddColumn(writer.apply(), "mytable", column, false, false);
ddlGen.alterTableAddColumn(writer, "mytable", column, false, false);
String ddl = writer.apply().getBuffer();
String ddl = writer.toString();
assertThat(ddl).contains("alter table mytable add col_name varchar2(20)");
}
@@ -72,9 +72,9 @@ public class BaseTableDdlTest {
column.setName("col_name");
column.setType("varchar(20)");
ddlGen.alterTableAddColumn(writer.apply(), "mytable", column, false, false);
ddlGen.alterTableAddColumn(writer, "mytable", column, false, false);
String ddl = writer.apply().getBuffer();
String ddl = writer.toString();
assertThat(ddl).contains("alter table mytable add column col_name String");
}
@@ -92,7 +92,7 @@ public class BaseTableDdlTest {
ddlGen.generate(writer, alterColumn);
String ddl = writer.apply().getBuffer();
String ddl = writer.applyPostAlter().getBuffer();
assertThat(ddl).contains("comment on column mytab.acol is 'my comment'");
}
@@ -105,11 +105,10 @@ public class BaseTableDdlTest {
column.setComment("some comment");
column.setType("int");
ddl.alterTableAddColumn(writer.apply(), "my_table", column, false, false);
assertEquals(
"alter table my_table add column my_column int;\n" +
"comment on column my_table.my_column is 'some comment';\n",
writer.apply().getBuffer());
ddl.alterTableAddColumn(writer, "my_table", column, false, false);
assertEquals("-- apply alter tables\nalter table my_table add column my_column int;\n"
+ "-- apply post alter\ncomment on column my_table.my_column is 'some comment';\n",
writer.toString());
}
@Test
@@ -125,7 +124,7 @@ public class BaseTableDdlTest {
ddlGen.generate(writer, addTableComment);
String ddl = writer.apply().getBuffer();
String ddl = writer.applyPostAlter().getBuffer();
assertThat(ddl).contains("comment on table mytab is 'my comment'");
}
@@ -142,7 +141,7 @@ public class BaseTableDdlTest {
ddlGen.generate(writer, addTableComment);
String ddl = writer.apply().getBuffer();
String ddl = writer.applyPostAlter().getBuffer();
assertThat(ddl).contains("alter table mytab comment = 'my comment'");
}
@@ -1,50 +0,0 @@
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
import io.ebean.DB;
import io.ebean.config.dbplatform.h2.H2Platform;
import io.ebeaninternal.api.SpiEbeanServer;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlOptions;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite;
import io.ebeaninternal.dbmigration.ddlgeneration.PlatformDdlBuilder;
import io.ebeaninternal.dbmigration.model.CurrentModel;
import io.ebeaninternal.dbmigration.model.MConfiguration;
import io.ebeaninternal.dbmigration.model.ModelContainer;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
public class H2HistoryDdlTest {
@Test
public void testRegenerateHistoryTriggers() throws Exception {
SpiEbeanServer ebeanServer = (SpiEbeanServer) DB.getDefault();
HistoryTableUpdate update = new HistoryTableUpdate("c_user");
update.add(HistoryTableUpdate.Change.ADD, "one");
update.add(HistoryTableUpdate.Change.DROP, "two");
CurrentModel currentModel = new CurrentModel(ebeanServer);
ModelContainer modelContainer = currentModel.read();
DdlWrite writer = new DdlWrite(new MConfiguration(), modelContainer, new DdlOptions());
H2Platform h2Platform = new H2Platform();
PlatformDdl h2Ddl = PlatformDdlBuilder.create(h2Platform);
h2Ddl.configure(ebeanServer.config());
h2Ddl.regenerateHistoryTriggers(writer, update);
assertThat(writer.applyHistoryView().isEmpty()).isFalse();
assertThat(writer.applyHistoryTrigger().isEmpty()).isFalse();
assertThat(writer.applyHistoryView().getBuffer())
.contains("create view")
.doesNotContain("create trigger");
assertThat(writer.applyHistoryTrigger().getBuffer())
.contains("add one")
.contains("create trigger")
.doesNotContain("create view");
assertThat(writer.dropAll().isEmpty()).isTrue();
}
}
@@ -13,8 +13,8 @@ public class HanaDdlTest {
public void alterTableDropColumn() {
HanaColumnStoreDdl ddl = new HanaColumnStoreDdl(new HanaPlatform());
DdlWrite writer = new DdlWrite();
ddl.alterTableDropColumn(writer.apply(), "my_table", "my_column");
assertEquals("CALL usp_ebean_drop_column('my_table', 'my_column');\n", writer.apply().getBuffer());
ddl.alterTableDropColumn(writer, "my_table", "my_column");
assertEquals("-- apply alter tables\nCALL usp_ebean_drop_column('my_table', 'my_column');\n", writer.toString());
}
@Test
@@ -33,7 +33,9 @@ public class HanaDdlTest {
column.setCheckConstraintName("check_constraint");
column.setHistoryExclude(Boolean.TRUE);
column.setIdentity(Boolean.TRUE);
ddl.alterTableAddColumn(writer.apply(), "my_table", column, false, "1");
assertEquals("alter table my_table add ( my_column int default 1 not null);\nalter table my_table add constraint check_constraint CHECK(my_column > 0);\n", writer.apply().getBuffer());
ddl.alterTableAddColumn(writer, "my_table", column, false, "1");
assertEquals(
"-- apply alter tables\nalter table my_table add (my_column int default 1 not null);\n-- apply post alter\nalter table my_table add constraint check_constraint CHECK(my_column > 0);\n",
writer.toString());
}
}
@@ -1,32 +0,0 @@
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
public class HistoryTableUpdateTest {
@Test
public void testDescription() throws Exception {
HistoryTableUpdate upd = new HistoryTableUpdate("mytab");
upd.add(HistoryTableUpdate.Change.ADD, "two");
upd.add(HistoryTableUpdate.Change.DROP, "four");
assertThat(upd.description()).isEqualTo("[add two, drop four]");
}
@Test
public void testDescription_withIncludeExclude() throws Exception {
HistoryTableUpdate upd = new HistoryTableUpdate("mytab");
upd.add(HistoryTableUpdate.Change.ADD, "two");
upd.add(HistoryTableUpdate.Change.INCLUDE, "five");
upd.add(HistoryTableUpdate.Change.EXCLUDE, "six");
upd.add(HistoryTableUpdate.Change.DROP, "four");
assertThat(upd.description()).isEqualTo("[add two, include five, exclude six, drop four]");
}
}
@@ -3,6 +3,7 @@ package io.ebeaninternal.dbmigration.ddlgeneration.platform;
import io.ebean.DB;
import io.ebean.config.DatabaseConfig;
import io.ebean.config.dbplatform.IdType;
import io.ebean.config.dbplatform.db2.DB2LuwPlatform;
import io.ebean.config.dbplatform.h2.H2Platform;
import io.ebean.config.dbplatform.hana.HanaPlatform;
import io.ebean.config.dbplatform.mysql.MySqlPlatform;
@@ -14,12 +15,11 @@ import io.ebeaninternal.dbmigration.ddlgeneration.PlatformDdlBuilder;
import io.ebeaninternal.dbmigration.migration.AlterColumn;
import io.ebeaninternal.dbmigration.migration.AlterForeignKey;
import io.ebeaninternal.dbmigration.migration.Column;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.assertj.core.api.SoftAssertions;
public class PlatformDdl_AlterColumnTest {
@@ -31,229 +31,424 @@ public class PlatformDdl_AlterColumnTest {
private final PlatformDdl oraDdl = PlatformDdlBuilder.create(new OraclePlatform());
private final PlatformDdl sqlServerDdl = PlatformDdlBuilder.create(new SqlServer17Platform());
private final PlatformDdl hanaDdl = PlatformDdlBuilder.create(new HanaPlatform());
private final PlatformDdl db2Ddl = PlatformDdlBuilder.create(new DB2LuwPlatform());
private final SoftAssertions softly = new SoftAssertions();
{
DatabaseConfig serverConfig = DB.getDefault().pluginApi().config();
sqlServerDdl.configure(serverConfig);
}
AlterColumn alterNotNull() {
AlterColumn alterColumn = new AlterColumn();
alterColumn.setTableName("mytab");
alterColumn.setColumnName("acol");
alterColumn.setCurrentType("varchar(5)");
alterColumn.setNotnull(Boolean.TRUE);
return alterColumn;
@AfterEach
void assertAll() {
softly.assertAll();
}
@Test
public void convertArrayType_default() {
assertThat(mysqlDdl.convertArrayType("varchar[](90)")).isEqualTo("varchar(90)");
assertThat(mysqlDdl.convertArrayType("integer[](60)")).isEqualTo("varchar(60)");
assertThat(mysqlDdl.convertArrayType("varchar[]")).isEqualTo("varchar(1000)");
assertThat(mysqlDdl.convertArrayType("integer[]")).isEqualTo("varchar(1000)");
softly.assertThat(mysqlDdl.convertArrayType("varchar[](90)")).isEqualTo("varchar(90)");
softly.assertThat(mysqlDdl.convertArrayType("integer[](60)")).isEqualTo("varchar(60)");
softly.assertThat(mysqlDdl.convertArrayType("varchar[]")).isEqualTo("varchar(1000)");
softly.assertThat(mysqlDdl.convertArrayType("integer[]")).isEqualTo("varchar(1000)");
}
@Test
public void convertArrayType_h2() {
if (useV1Syntax) {
assertThat(h2Ddl.convertArrayType("varchar[](90)")).isEqualTo("array");
assertThat(h2Ddl.convertArrayType("integer[](60)")).isEqualTo("array");
assertThat(h2Ddl.convertArrayType("varchar[]")).isEqualTo("array");
assertThat(h2Ddl.convertArrayType("integer[]")).isEqualTo("array");
softly.assertThat(h2Ddl.convertArrayType("varchar[](90)")).isEqualTo("array");
softly.assertThat(h2Ddl.convertArrayType("integer[](60)")).isEqualTo("array");
softly.assertThat(h2Ddl.convertArrayType("varchar[]")).isEqualTo("array");
softly.assertThat(h2Ddl.convertArrayType("integer[]")).isEqualTo("array");
} else {
assertThat(h2Ddl.convertArrayType("varchar[](90)")).isEqualTo("varchar array");
assertThat(h2Ddl.convertArrayType("integer[](60)")).isEqualTo("integer array");
assertThat(h2Ddl.convertArrayType("varchar[]")).isEqualTo("varchar array");
assertThat(h2Ddl.convertArrayType("integer[]")).isEqualTo("integer array");
softly.assertThat(h2Ddl.convertArrayType("varchar[](90)")).isEqualTo("varchar array");
softly.assertThat(h2Ddl.convertArrayType("integer[](60)")).isEqualTo("integer array");
softly.assertThat(h2Ddl.convertArrayType("varchar[]")).isEqualTo("varchar array");
softly.assertThat(h2Ddl.convertArrayType("integer[]")).isEqualTo("integer array");
}
}
@Test
public void convertArrayType_postgres() {
assertThat(pgDdl.convertArrayType("varchar[](90)")).isEqualTo("varchar[]");
assertThat(pgDdl.convertArrayType("integer[](60)")).isEqualTo("integer[]");
assertThat(pgDdl.convertArrayType("varchar[]")).isEqualTo("varchar[]");
assertThat(pgDdl.convertArrayType("integer[]")).isEqualTo("integer[]");
softly.assertThat(pgDdl.convertArrayType("varchar[](90)")).isEqualTo("varchar[]");
softly.assertThat(pgDdl.convertArrayType("integer[](60)")).isEqualTo("integer[]");
softly.assertThat(pgDdl.convertArrayType("varchar[]")).isEqualTo("varchar[]");
softly.assertThat(pgDdl.convertArrayType("integer[]")).isEqualTo("integer[]");
}
@Test
public void convertArrayType_hana() {
assertThat(hanaDdl.convertArrayType("varchar[](90)")).isEqualTo("nvarchar(255) array(90)");
assertThat(hanaDdl.convertArrayType("integer[](60)")).isEqualTo("integer array(60)");
assertThat(hanaDdl.convertArrayType("varchar[]")).isEqualTo("nvarchar(255) array");
assertThat(hanaDdl.convertArrayType("integer[]")).isEqualTo("integer array");
softly.assertThat(hanaDdl.convertArrayType("varchar[](90)")).isEqualTo("nvarchar(255) array(90)");
softly.assertThat(hanaDdl.convertArrayType("integer[](60)")).isEqualTo("integer array(60)");
softly.assertThat(hanaDdl.convertArrayType("varchar[]")).isEqualTo("nvarchar(255) array");
softly.assertThat(hanaDdl.convertArrayType("integer[]")).isEqualTo("integer array");
}
@Test
public void testAlterColumnBaseAttributes() {
AlterColumn alterColumn = alterNotNull();
assertNull(h2Ddl.alterColumnBaseAttributes(alterColumn));
assertNull(pgDdl.alterColumnBaseAttributes(alterColumn));
assertNull(oraDdl.alterColumnBaseAttributes(alterColumn));
AlterColumn alter = new AlterColumn();
alter.setTableName("mytab");
alter.setColumnName("acol");
alter.setCurrentType("varchar(5)");
alter.setCurrentDefaultValue("'ho'");
alter.setCurrentNotnull(Boolean.FALSE);
String sql = mysqlDdl.alterColumnBaseAttributes(alterColumn);
assertEquals("alter table mytab modify acol varchar(5) not null", sql);
// alter all attributes
alter.setNotnull(Boolean.TRUE); // -> alter to not null
alter.setDefaultValue("'hi'"); // and set default
alter.setType("varchar(50)"); // and alter type
sql = sqlServerDdl.alterColumnBaseAttributes(alterColumn);
assertEquals("alter table mytab alter column acol nvarchar(5) not null", sql);
String sql = alterColumn(h2Ddl, alter);
softly.assertThat(sql).isEqualTo("-- apply alter tables\n"
+ "alter table mytab alter column acol varchar(50);\n"
+ "alter table mytab alter column acol set default 'hi';\n"
+ "alter table mytab alter column acol set not null;\n");
sql = hanaDdl.alterColumnBaseAttributes(alterColumn);
assertEquals("alter table mytab alter ( acol nvarchar(5) not null)", sql);
sql = alterColumn(pgDdl, alter);
softly.assertThat(sql).isEqualTo("-- apply alter tables\n"
+ "alter table mytab alter column acol type varchar(50) using acol::varchar(50);\n"
+ "alter table mytab alter column acol set default 'hi';\n"
+ "alter table mytab alter column acol set not null;\n");
alterColumn.setNotnull(Boolean.FALSE);
sql = mysqlDdl.alterColumnBaseAttributes(alterColumn);
assertEquals("alter table mytab modify acol varchar(5)", sql);
sql = alterColumn(oraDdl, alter);
softly.assertThat(sql).isEqualTo("-- apply alter tables\n"
+ "alter table mytab modify acol varchar2(50);\n"
+ "alter table mytab modify acol default 'hi';\n"
+ "alter table mytab modify acol not null;\n");
sql = hanaDdl.alterColumnBaseAttributes(alterColumn);
assertEquals("alter table mytab alter ( acol nvarchar(5))", sql);
sql = alterColumn(mysqlDdl, alter);
softly.assertThat(sql).isEqualTo("-- apply alter tables\n"
+ "alter table mytab modify acol varchar(50) not null default 'hi';\n");
alterColumn.setNotnull(null);
alterColumn.setType("varchar(100)");
sql = alterColumn(sqlServerDdl, alter);
softly.assertThat(sql).isEqualTo("-- apply alter tables\n"
+ "EXEC usp_ebean_drop_default_constraint mytab, acol;\n"
+ "alter table mytab alter column acol nvarchar(50) not null;\n"
+ "alter table mytab add default 'hi' for acol;\n");
sql = mysqlDdl.alterColumnBaseAttributes(alterColumn);
assertEquals("alter table mytab modify acol varchar(100)", sql);
sql = alterColumn(hanaDdl, alter);
softly.assertThat(sql).isEqualTo("-- apply alter tables\n"
+ "alter table mytab alter (acol nvarchar(50) default 'hi' not null);\n");
sql = hanaDdl.alterColumnBaseAttributes(alterColumn);
assertEquals("alter table mytab alter ( acol nvarchar(100))", sql);
sql = alterColumn(db2Ddl, alter);
softly.assertThat(sql).isEqualTo("-- apply alter tables\n"
+ "alter table mytab alter column acol set data type varchar(50);\n"
+ "alter table mytab alter column acol set default 'hi';\n"
+ "alter table mytab alter column acol set not null;\n"
+ "call sysproc.admin_cmd('reorg table mytab');\n");
alterColumn.setCurrentNotnull(Boolean.TRUE);
sql = mysqlDdl.alterColumnBaseAttributes(alterColumn);
assertEquals("alter table mytab modify acol varchar(100) not null", sql);
//
alter.setCurrentNotnull(Boolean.TRUE);
alter.setNotnull(Boolean.FALSE);
alter.setDefaultValue("DROP DEFAULT");
alter.setType(null); // do not alter type
sql = alterColumn(h2Ddl, alter);
softly.assertThat(sql).isEqualTo("-- apply alter tables\n"
+ "alter table mytab alter column acol drop default;\n"
+ "alter table mytab alter column acol set null;\n");
sql = alterColumn(pgDdl, alter);
softly.assertThat(sql).isEqualTo("-- apply alter tables\n"
+ "alter table mytab alter column acol drop default;\n"
+ "alter table mytab alter column acol drop not null;\n");
sql = alterColumn(oraDdl, alter);
softly.assertThat(sql).isEqualTo("-- apply alter tables\n"
+ "alter table mytab modify acol default null;\n"
+ "alter table mytab modify acol null;\n");
sql = alterColumn(mysqlDdl, alter);
softly.assertThat(sql).isEqualTo("-- apply alter tables\n"
+ "alter table mytab modify acol varchar(5);\n");
sql = alterColumn(sqlServerDdl, alter);
softly.assertThat(sql).isEqualTo("-- apply alter tables\n"
+ "EXEC usp_ebean_drop_default_constraint mytab, acol;\n"
+ "alter table mytab alter column acol nvarchar(5);\n");
sql = alterColumn(hanaDdl, alter);
softly.assertThat(sql).isEqualTo("-- apply alter tables\n"
+ "alter table mytab alter (acol nvarchar(5) default null);\n");
sql = alterColumn(db2Ddl, alter);
softly.assertThat(sql).isEqualTo("-- apply alter tables\n"
+ "alter table mytab alter column acol drop default;\n"
+ "alter table mytab alter column acol drop not null;\n");
sql = hanaDdl.alterColumnBaseAttributes(alterColumn);
assertEquals("alter table mytab alter ( acol nvarchar(100) not null)", sql);
}
@Test
public void testAlterColumnType() {
String sql = h2Ddl.alterColumnType("mytab", "acol", "varchar(20)");
assertEquals("alter table mytab alter column acol varchar(20)", sql);
AlterColumn alter = new AlterColumn();
alter.setTableName("mytab");
alter.setColumnName("acol");
alter.setCurrentType("integer");
alter.setType("varchar(20)");
sql = pgDdl.alterColumnType("mytab", "acol", "varchar(20)");
assertEquals("alter table mytab alter column acol type varchar(20) using acol::varchar(20)", sql);
sql = pgDdl.alterColumnType("mytab", "acol", "bigint");
assertEquals("alter table mytab alter column acol type bigint using acol::bigint", sql);
String sql = alterColumn(h2Ddl, alter);
softly.assertThat(sql).isEqualTo("-- apply alter tables\n"
+ "alter table mytab alter column acol varchar(20);\n");
sql = oraDdl.alterColumnType("mytab", "acol", "varchar(20)");
assertEquals("alter table mytab modify acol varchar2(20)", sql);
sql = alterColumn(pgDdl, alter);
softly.assertThat(sql).isEqualTo("-- apply alter tables\n"
+ "alter table mytab alter column acol type varchar(20) using acol::varchar(20);\n");
sql = mysqlDdl.alterColumnType("mytab", "acol", "varchar(20)");
assertNull(sql);
sql = alterColumn(oraDdl, alter);
softly.assertThat(sql).isEqualTo("-- apply alter tables\n"
+ "alter table mytab modify acol varchar2(20);\n");
sql = sqlServerDdl.alterColumnType("mytab", "acol", "varchar(20)");
assertNull(sql);
sql = alterColumn(mysqlDdl, alter);
softly.assertThat(sql).isEqualTo("-- apply alter tables\n"
+ "alter table mytab modify acol varchar(20);\n");
sql = alterColumn(sqlServerDdl, alter);
softly.assertThat(sql).isEqualTo("-- apply alter tables\n"
+ "alter table mytab alter column acol nvarchar(20);\n");
sql = alterColumn(hanaDdl, alter);
softly.assertThat(sql).isEqualTo("-- apply alter tables\n"
+ "alter table mytab alter (acol nvarchar(20));\n");
sql = alterColumn(db2Ddl, alter);
softly.assertThat(sql).isEqualTo("-- apply alter tables\n"
+ "alter table mytab alter column acol set data type varchar(20);\n"
// Note, this reorg may be not necessary when only length attribute is alterd
// but this is currently not implemented.
+ "call sysproc.admin_cmd('reorg table mytab');\n");
alter.setType("bigint");
sql = alterColumn(pgDdl, alter);
softly.assertThat(sql).isEqualTo("-- apply alter tables\n"
+ "alter table mytab alter column acol type bigint using acol::bigint;\n");
alter.setCurrentType("bigint");
alter.setType("integer");
sql = alterColumn(hanaDdl, alter);
softly.assertThat(sql).isEqualTo("-- apply alter tables\n"
+ "alter table mytab alter (acol decimal);\n"
+ "alter table mytab alter (acol integer);\n");
alter.setCurrentType("varchar(20)");
alter.setType("varchar(10)");
sql = alterColumn(hanaDdl, alter);
softly.assertThat(sql).isEqualTo("-- apply alter tables\n"
+ "alter table mytab alter (acol nclob);\n"
+ "alter table mytab alter (acol nvarchar(10));\n");
sql = hanaDdl.alterColumnType("mytab", "acol", "varchar(20)");
assertNull(sql);
}
@Test
public void testAlterColumnNotnull() {
AlterColumn alter = new AlterColumn();
alter.setTableName("mytab");
alter.setColumnName("acol");
alter.setCurrentType("varchar(20)");
alter.setCurrentNotnull(Boolean.FALSE);
alter.setNotnull(Boolean.TRUE);
String sql = h2Ddl.alterColumnNotnull("mytab", "acol", true);
assertEquals("alter table mytab alter column acol set not null", sql);
String sql = alterColumn(h2Ddl, alter);
softly.assertThat(sql).isEqualTo("-- apply alter tables\n"
+ "alter table mytab alter column acol set not null;\n");
sql = pgDdl.alterColumnNotnull("mytab", "acol", true);
assertEquals("alter table mytab alter column acol set not null", sql);
sql = alterColumn(pgDdl, alter);
softly.assertThat(sql).isEqualTo("-- apply alter tables\n"
+ "alter table mytab alter column acol set not null;\n");
sql = oraDdl.alterColumnNotnull("mytab", "acol", true);
assertEquals("alter table mytab modify acol not null", sql);
sql = alterColumn(oraDdl, alter);
softly.assertThat(sql).isEqualTo("-- apply alter tables\n"
+ "alter table mytab modify acol not null;\n");
sql = mysqlDdl.alterColumnNotnull("mytab", "acol", true);
assertNull(sql);
sql = alterColumn(mysqlDdl, alter);
softly.assertThat(sql).isEqualTo("-- apply alter tables\n"
+ "alter table mytab modify acol varchar(20) not null;\n");
sql = sqlServerDdl.alterColumnNotnull("mytab", "acol", true);
assertNull(sql);
sql = alterColumn(sqlServerDdl, alter);
softly.assertThat(sql).isEqualTo("-- apply alter tables\n"
+ "alter table mytab alter column acol nvarchar(20) not null;\n");
sql = alterColumn(hanaDdl, alter);
softly.assertThat(sql).isEqualTo("-- apply alter tables\n"
+ "alter table mytab alter (acol nvarchar(20) not null);\n");
sql = alterColumn(db2Ddl, alter);
softly.assertThat(sql).isEqualTo("-- apply alter tables\n"
+ "alter table mytab alter column acol set not null;\n"
+ "call sysproc.admin_cmd('reorg table mytab');\n");
sql = hanaDdl.alterColumnNotnull("mytab", "acol", true);
assertNull(sql);
}
@Test
public void testAlterColumnNull() {
String sql = h2Ddl.alterColumnNotnull("mytab", "acol", false);
assertEquals("alter table mytab alter column acol set null", sql);
AlterColumn alter = new AlterColumn();
alter.setTableName("mytab");
alter.setColumnName("acol");
alter.setCurrentType("varchar(20)");
alter.setCurrentDefaultValue("'hi'");
alter.setCurrentNotnull(Boolean.TRUE);
sql = pgDdl.alterColumnNotnull("mytab", "acol", false);
assertEquals("alter table mytab alter column acol drop not null", sql);
alter.setNotnull(Boolean.FALSE);
String sql = alterColumn(h2Ddl, alter);
softly.assertThat(sql).isEqualTo("-- apply alter tables\n"
+ "alter table mytab alter column acol set null;\n");
sql = oraDdl.alterColumnNotnull("mytab", "acol", false);
assertEquals("alter table mytab modify acol null", sql);
sql = alterColumn(pgDdl, alter);
softly.assertThat(sql).isEqualTo("-- apply alter tables\n"
+ "alter table mytab alter column acol drop not null;\n");
sql = mysqlDdl.alterColumnNotnull("mytab", "acol", false);
assertNull(sql);
sql = alterColumn(oraDdl, alter);
softly.assertThat(sql).isEqualTo("-- apply alter tables\n"
+ "alter table mytab modify acol null;\n");
sql = sqlServerDdl.alterColumnNotnull("mytab", "acol", false);
assertNull(sql);
sql = alterColumn(mysqlDdl, alter);
softly.assertThat(sql).isEqualTo("-- apply alter tables\n"
+ "alter table mytab modify acol varchar(20) default 'hi';\n");
sql = hanaDdl.alterColumnNotnull("mytab", "acol", false);
assertNull(sql);
sql = alterColumn(sqlServerDdl, alter);
softly.assertThat(sql).isEqualTo("-- apply alter tables\n"
+ "EXEC usp_ebean_drop_default_constraint mytab, acol;\n"
+ "alter table mytab alter column acol nvarchar(20);\n"
+ "alter table mytab add default 'hi' for acol;\n");
sql = alterColumn(hanaDdl, alter);
softly.assertThat(sql).isEqualTo("-- apply alter tables\n"
+ "alter table mytab alter (acol nvarchar(20) default 'hi');\n");
sql = alterColumn(db2Ddl, alter);
softly.assertThat(sql).isEqualTo("-- apply alter tables\n"
+ "alter table mytab alter column acol drop not null;\n");
}
@Test
public void testAlterColumnDefaultValue() {
public void testAlterColumnAddDefaultValue() {
AlterColumn alter = new AlterColumn();
alter.setTableName("mytab");
alter.setColumnName("acol");
alter.setCurrentType("varchar(20)");
alter.setCurrentNotnull(Boolean.TRUE);
alter.setDefaultValue("'hi'");
String sql = h2Ddl.alterColumnDefaultValue("mytab", "acol", "'hi'");
assertEquals("alter table mytab alter column acol set default 'hi'", sql);
String sql = alterColumn(h2Ddl, alter);
softly.assertThat(sql).isEqualTo("-- apply alter tables\n"
+ "alter table mytab alter column acol set default 'hi';\n");
sql = pgDdl.alterColumnDefaultValue("mytab", "acol", "'hi'");
assertEquals("alter table mytab alter column acol set default 'hi'", sql);
sql = alterColumn(pgDdl, alter);
softly.assertThat(sql).isEqualTo("-- apply alter tables\n"
+ "alter table mytab alter column acol set default 'hi';\n");
sql = oraDdl.alterColumnDefaultValue("mytab", "acol", "'hi'");
assertEquals("alter table mytab modify acol default 'hi'", sql);
sql = alterColumn(oraDdl, alter);
softly.assertThat(sql).isEqualTo("-- apply alter tables\n"
+ "alter table mytab modify acol default 'hi';\n");
sql = mysqlDdl.alterColumnDefaultValue("mytab", "acol", "'hi'");
assertEquals("alter table mytab alter acol set default 'hi'", sql);
sql = alterColumn(mysqlDdl, alter);
softly.assertThat(sql).isEqualTo("-- apply alter tables\n"
+ "alter table mytab alter acol set default 'hi';\n");
sql = sqlServerDdl.alterColumnDefaultValue("mytab", "acol", "'hi'");
assertEquals("alter table mytab add default 'hi' for acol", sql);
sql = alterColumn(sqlServerDdl, alter);
softly.assertThat(sql).isEqualTo("-- apply alter tables\n"
+ "EXEC usp_ebean_drop_default_constraint mytab, acol;\n"
+ "alter table mytab add default 'hi' for acol;\n");
boolean exceptionCaught = false;
try {
hanaDdl.alterColumnDefaultValue("mytab", "acol", "'hi'");
} catch (UnsupportedOperationException e) {
exceptionCaught = true;
}
assertTrue(exceptionCaught);
sql = alterColumn(hanaDdl, alter);
softly.assertThat(sql).isEqualTo("-- apply alter tables\n"
+ "alter table mytab alter (acol nvarchar(20) default 'hi' not null);\n");
sql = alterColumn(db2Ddl, alter);
softly.assertThat(sql).isEqualTo("-- apply alter tables\n"
+ "alter table mytab alter column acol set default 'hi';\n");
}
@Test
public void testAlterColumnChangeDefaultValue() {
AlterColumn alter = new AlterColumn();
alter.setTableName("mytab");
alter.setColumnName("acol");
alter.setCurrentType("varchar(20)");
alter.setCurrentNotnull(Boolean.TRUE);
alter.setDefaultValue("'ho'");
alter.setDefaultValue("'hi'");
String sql = alterColumn(h2Ddl, alter);
softly.assertThat(sql).isEqualTo("-- apply alter tables\n"
+ "alter table mytab alter column acol set default 'hi';\n");
sql = alterColumn(pgDdl, alter);
softly.assertThat(sql).isEqualTo("-- apply alter tables\n"
+ "alter table mytab alter column acol set default 'hi';\n");
sql = alterColumn(oraDdl, alter);
softly.assertThat(sql).isEqualTo("-- apply alter tables\n"
+ "alter table mytab modify acol default 'hi';\n");
sql = alterColumn(mysqlDdl, alter);
softly.assertThat(sql).isEqualTo("-- apply alter tables\n"
+ "alter table mytab alter acol set default 'hi';\n");
sql = alterColumn(sqlServerDdl, alter);
softly.assertThat(sql).isEqualTo("-- apply alter tables\n"
+ "EXEC usp_ebean_drop_default_constraint mytab, acol;\n"
+ "alter table mytab add default 'hi' for acol;\n");
sql = alterColumn(hanaDdl, alter);
softly.assertThat(sql).isEqualTo("-- apply alter tables\n"
+ "alter table mytab alter (acol nvarchar(20) default 'hi' not null);\n");
sql = alterColumn(db2Ddl, alter);
softly.assertThat(sql).isEqualTo("-- apply alter tables\n"
+ "alter table mytab alter column acol set default 'hi';\n");
}
@Test
public void testAlterColumnDropDefault() {
String sql = h2Ddl.alterColumnDefaultValue("mytab", "acol", "DROP DEFAULT");
assertEquals("alter table mytab alter column acol drop default", sql);
AlterColumn alter = new AlterColumn();
alter.setTableName("mytab");
alter.setColumnName("acol");
alter.setCurrentType("varchar(20)");
alter.setCurrentNotnull(Boolean.TRUE);
alter.setCurrentDefaultValue("'hi'");
alter.setDefaultValue("DROP DEFAULT");
sql = pgDdl.alterColumnDefaultValue("mytab", "acol", "DROP DEFAULT");
assertEquals("alter table mytab alter column acol drop default", sql);
String sql = alterColumn(h2Ddl, alter);
softly.assertThat(sql).isEqualTo("-- apply alter tables\n"
+ "alter table mytab alter column acol drop default;\n");
sql = oraDdl.alterColumnDefaultValue("mytab", "acol", "DROP DEFAULT");
assertEquals("alter table mytab modify acol default null", sql);
sql = alterColumn(pgDdl, alter);
softly.assertThat(sql).isEqualTo("-- apply alter tables\n"
+ "alter table mytab alter column acol drop default;\n");
sql = mysqlDdl.alterColumnDefaultValue("mytab", "acol", "DROP DEFAULT");
assertEquals("alter table mytab alter acol drop default", sql);
sql = alterColumn(oraDdl, alter);
softly.assertThat(sql).isEqualTo("-- apply alter tables\n"
+ "alter table mytab modify acol default null;\n");
sql = sqlServerDdl.alterColumnDefaultValue("mytab", "acol", "DROP DEFAULT");
assertEquals("EXEC usp_ebean_drop_default_constraint mytab, acol", sql);
sql = alterColumn(mysqlDdl, alter);
softly.assertThat(sql).isEqualTo("-- apply alter tables\n"
+ "alter table mytab alter acol drop default;\n");
sql = alterColumn(sqlServerDdl, alter);
softly.assertThat(sql).isEqualTo("-- apply alter tables\n"
+ "EXEC usp_ebean_drop_default_constraint mytab, acol;\n");
sql = alterColumn(hanaDdl, alter);
softly.assertThat(sql).isEqualTo("-- apply alter tables\n"
+ "alter table mytab alter (acol nvarchar(20) default null not null);\n");
sql = alterColumn(db2Ddl, alter);
softly.assertThat(sql).isEqualTo("-- apply alter tables\n"
+ "alter table mytab alter column acol drop default;\n");
boolean exceptionCaught = false;
try {
hanaDdl.alterColumnDefaultValue("mytab", "acol", "DROP DEFAULT");
} catch (UnsupportedOperationException e) {
exceptionCaught = true;
}
assertTrue(exceptionCaught);
}
@Test
public void oracle_alterTableAddColumn() {
DdlWrite writer = new DdlWrite();
oraDdl.alterTableAddColumn(writer.apply(), "my_table", simpleColumn(), false, "1");
assertThat(writer.apply().getBuffer()).isEqualTo("alter table my_table add my_column int default 1 not null;\n");
oraDdl.alterTableAddColumn(writer, "my_table", simpleColumn(), false, "1");
softly.assertThat(writer.toString())
.isEqualTo("-- apply alter tables\n"
+ "alter table my_table add my_column int default 1 not null;\n");
}
private Column simpleColumn() {
@@ -267,79 +462,148 @@ public class PlatformDdl_AlterColumnTest {
@Test
public void useIdentityType_h2() {
assertEquals(h2Ddl.useIdentityType(null), IdType.IDENTITY);
assertEquals(h2Ddl.useIdentityType(IdType.SEQUENCE), IdType.SEQUENCE);
assertEquals(h2Ddl.useIdentityType(IdType.IDENTITY), IdType.IDENTITY);
assertEquals(h2Ddl.useIdentityType(IdType.GENERATOR), IdType.GENERATOR);
assertEquals(h2Ddl.useIdentityType(IdType.EXTERNAL), IdType.EXTERNAL);
softly.assertThat(h2Ddl.useIdentityType(null)).isEqualTo(IdType.IDENTITY);
softly.assertThat(h2Ddl.useIdentityType(IdType.SEQUENCE)).isEqualTo(IdType.SEQUENCE);
softly.assertThat(h2Ddl.useIdentityType(IdType.IDENTITY)).isEqualTo(IdType.IDENTITY);
softly.assertThat(h2Ddl.useIdentityType(IdType.GENERATOR)).isEqualTo(IdType.GENERATOR);
softly.assertThat(h2Ddl.useIdentityType(IdType.EXTERNAL)).isEqualTo(IdType.EXTERNAL);
}
@Test
public void useIdentityType_postgres() {
assertEquals(pgDdl.useIdentityType(IdType.GENERATOR), IdType.GENERATOR);
assertEquals(pgDdl.useIdentityType(IdType.EXTERNAL), IdType.EXTERNAL);
softly.assertThat(pgDdl.useIdentityType(IdType.GENERATOR)).isEqualTo(IdType.GENERATOR);
softly.assertThat(pgDdl.useIdentityType(IdType.EXTERNAL)).isEqualTo(IdType.EXTERNAL);
assertEquals(pgDdl.useIdentityType(null), IdType.IDENTITY);
assertEquals(pgDdl.useIdentityType(IdType.SEQUENCE), IdType.SEQUENCE);
assertEquals(pgDdl.useIdentityType(IdType.IDENTITY), IdType.IDENTITY);
softly.assertThat(pgDdl.useIdentityType(null)).isEqualTo(IdType.IDENTITY);
softly.assertThat(pgDdl.useIdentityType(IdType.SEQUENCE)).isEqualTo(IdType.SEQUENCE);
softly.assertThat(pgDdl.useIdentityType(IdType.IDENTITY)).isEqualTo(IdType.IDENTITY);
}
@Test
public void useIdentityType_mysql() {
assertEquals(mysqlDdl.useIdentityType(null), IdType.IDENTITY);
assertEquals(mysqlDdl.useIdentityType(IdType.SEQUENCE), IdType.IDENTITY);
assertEquals(mysqlDdl.useIdentityType(IdType.IDENTITY), IdType.IDENTITY);
assertEquals(mysqlDdl.useIdentityType(IdType.GENERATOR), IdType.GENERATOR);
assertEquals(mysqlDdl.useIdentityType(IdType.EXTERNAL), IdType.EXTERNAL);
softly.assertThat(mysqlDdl.useIdentityType(null)).isEqualTo(IdType.IDENTITY);
softly.assertThat(mysqlDdl.useIdentityType(IdType.SEQUENCE)).isEqualTo(IdType.IDENTITY);
softly.assertThat(mysqlDdl.useIdentityType(IdType.IDENTITY)).isEqualTo(IdType.IDENTITY);
softly.assertThat(mysqlDdl.useIdentityType(IdType.GENERATOR)).isEqualTo(IdType.GENERATOR);
softly.assertThat(mysqlDdl.useIdentityType(IdType.EXTERNAL)).isEqualTo(IdType.EXTERNAL);
}
@Test
public void useIdentityType_oracle() {
assertEquals(oraDdl.useIdentityType(null), IdType.IDENTITY);
assertEquals(oraDdl.useIdentityType(IdType.SEQUENCE), IdType.SEQUENCE);
assertEquals(oraDdl.useIdentityType(IdType.IDENTITY), IdType.IDENTITY);
assertEquals(oraDdl.useIdentityType(IdType.GENERATOR), IdType.GENERATOR);
assertEquals(oraDdl.useIdentityType(IdType.EXTERNAL), IdType.EXTERNAL);
softly.assertThat(oraDdl.useIdentityType(null)).isEqualTo(IdType.IDENTITY);
softly.assertThat(oraDdl.useIdentityType(IdType.SEQUENCE)).isEqualTo(IdType.SEQUENCE);
softly.assertThat(oraDdl.useIdentityType(IdType.IDENTITY)).isEqualTo(IdType.IDENTITY);
softly.assertThat(oraDdl.useIdentityType(IdType.GENERATOR)).isEqualTo(IdType.GENERATOR);
softly.assertThat(oraDdl.useIdentityType(IdType.EXTERNAL)).isEqualTo(IdType.EXTERNAL);
}
@Test
public void useIdentityType_hana() {
assertEquals(hanaDdl.useIdentityType(null), IdType.IDENTITY);
assertEquals(hanaDdl.useIdentityType(IdType.SEQUENCE), IdType.IDENTITY);
assertEquals(hanaDdl.useIdentityType(IdType.IDENTITY), IdType.IDENTITY);
assertEquals(hanaDdl.useIdentityType(IdType.GENERATOR), IdType.GENERATOR);
assertEquals(hanaDdl.useIdentityType(IdType.EXTERNAL), IdType.EXTERNAL);
softly.assertThat(hanaDdl.useIdentityType(null)).isEqualTo(IdType.IDENTITY);
softly.assertThat(hanaDdl.useIdentityType(IdType.SEQUENCE)).isEqualTo(IdType.IDENTITY);
softly.assertThat(hanaDdl.useIdentityType(IdType.IDENTITY)).isEqualTo(IdType.IDENTITY);
softly.assertThat(hanaDdl.useIdentityType(IdType.GENERATOR)).isEqualTo(IdType.GENERATOR);
softly.assertThat(hanaDdl.useIdentityType(IdType.EXTERNAL)).isEqualTo(IdType.EXTERNAL);
}
@Test
public void appendForeignKeySuffix_when_defaults() {
assertThat(alterFkey(null, null)).isEqualTo(" on delete restrict on update restrict");
softly.assertThat(alterFkey(h2Ddl, null, null)).isEqualTo(" on delete restrict on update restrict");
softly.assertThat(alterFkey(pgDdl, null, null)).isEqualTo(" on delete restrict on update restrict");
softly.assertThat(alterFkey(mysqlDdl, null, null)).isEqualTo(" on delete restrict on update restrict");
softly.assertThat(alterFkey(oraDdl, null, null)).isEqualTo("");
softly.assertThat(alterFkey(sqlServerDdl, null, null)).isEqualTo("");
softly.assertThat(alterFkey(hanaDdl, null, null)).isEqualTo(" on delete restrict on update restrict");
softly.assertThat(alterFkey(db2Ddl, null, null)).isEqualTo(" on delete restrict on update restrict");
}
@Test
public void appendForeignKeySuffix_when_RestrictSetNull() {
assertThat(alterFkey("RESTRICT", "SET_NULL")).isEqualTo(" on delete restrict on update set null");
softly.assertThat(alterFkey(h2Ddl, "RESTRICT", "SET_NULL"))
.isEqualTo(" on delete restrict on update set null");
softly.assertThat(alterFkey(pgDdl, "RESTRICT", "SET_NULL"))
.isEqualTo(" on delete restrict on update set null");
softly.assertThat(alterFkey(mysqlDdl, "RESTRICT", "SET_NULL"))
.isEqualTo(" on delete restrict on update set null");
softly.assertThat(alterFkey(oraDdl, "RESTRICT", "SET_NULL"))
.isEqualTo("");
softly.assertThat(alterFkey(sqlServerDdl, "RESTRICT", "SET_NULL"))
.isEqualTo(" on update set null");
softly.assertThat(alterFkey(hanaDdl, "RESTRICT", "SET_NULL"))
.isEqualTo(" on delete restrict on update set null");
softly.assertThat(alterFkey(db2Ddl, "RESTRICT", "SET_NULL"))
.isEqualTo(" on delete restrict on update set null");
}
@Test
public void appendForeignKeySuffix_when_SetNullRestrict() {
assertThat(alterFkey("SET_NULL", "RESTRICT")).isEqualTo(" on delete set null on update restrict");
softly.assertThat(alterFkey(h2Ddl, "SET_NULL", "RESTRICT"))
.isEqualTo(" on delete set null on update restrict");
softly.assertThat(alterFkey(pgDdl, "SET_NULL", "RESTRICT"))
.isEqualTo(" on delete set null on update restrict");
softly.assertThat(alterFkey(mysqlDdl, "SET_NULL", "RESTRICT"))
.isEqualTo(" on delete set null on update restrict");
softly.assertThat(alterFkey(oraDdl, "SET_NULL", "RESTRICT"))
.isEqualTo(" on delete set null");
softly.assertThat(alterFkey(sqlServerDdl, "SET_NULL", "RESTRICT"))
.isEqualTo(" on delete set null");
softly.assertThat(alterFkey(hanaDdl, "SET_NULL", "RESTRICT"))
.isEqualTo(" on delete set null on update restrict");
softly.assertThat(alterFkey(db2Ddl, "SET_NULL", "RESTRICT"))
.isEqualTo(" on delete set null on update restrict");
}
@Test
public void appendForeignKeySuffix_when_SetDefaultCascade() {
assertThat(alterFkey("SET_DEFAULT", "CASCADE")).isEqualTo(" on delete set default on update cascade");
softly.assertThat(alterFkey(h2Ddl, "SET_DEFAULT", "CASCADE"))
.isEqualTo(" on delete set default on update cascade");
softly.assertThat(alterFkey(pgDdl, "SET_DEFAULT", "CASCADE"))
.isEqualTo(" on delete set default on update cascade");
softly.assertThat(alterFkey(mysqlDdl, "SET_DEFAULT", "CASCADE"))
.isEqualTo(" on delete set default on update cascade");
softly.assertThat(alterFkey(oraDdl, "SET_DEFAULT", "CASCADE"))
.isEqualTo("");
softly.assertThat(alterFkey(sqlServerDdl, "SET_DEFAULT", "CASCADE"))
.isEqualTo(" on delete set default on update cascade");
softly.assertThat(alterFkey(hanaDdl, "SET_DEFAULT", "CASCADE"))
.isEqualTo(" on delete set default on update cascade");
softly.assertThat(alterFkey(db2Ddl, "SET_DEFAULT", "CASCADE"))
.isEqualTo(" on delete set default on update cascade");
}
private String alterFkey(String onDelete, String onUpdate) {
private String alterColumn(PlatformDdl ddl, AlterColumn alterColumn) {
DdlWrite write = new DdlWrite();
ddl.alterColumn(write, alterColumn);
return write.toString();
}
private String alterFkey(PlatformDdl ddl, String onDelete, String onUpdate) {
AlterForeignKey afk = new AlterForeignKey();
afk.setOnDelete(onDelete);
afk.setOnUpdate(onUpdate);
StringBuilder buffer = new StringBuilder();
h2Ddl.appendForeignKeySuffix(new WriteForeignKey(afk), buffer);
ddl.appendForeignKeySuffix(new WriteForeignKey(afk), buffer);
return buffer.toString();
}
@@ -42,7 +42,7 @@ public class PlatformDdl_dropUniqueConstraintTest {
sql = sqlServerDdl.alterTableDropUniqueConstraint("mytab", "uq_name");
assertEquals(
"IF EXISTS (SELECT name FROM sys.indexes WHERE object_id = OBJECT_ID('mytab','U') AND name = 'uq_name') drop index uq_name ON mytab;\n"
+ "IF (OBJECT_ID('uq_name', 'UQ') IS NOT NULL) alter table mytab drop constraint uq_name",
+ "IF OBJECT_ID('uq_name', 'UQ') IS NOT NULL alter table mytab drop constraint uq_name",
sql);
sql = mysqlDdl.alterTableDropUniqueConstraint("mytab", "uq_name");
@@ -1,3 +1,4 @@
-- apply changes
create table foo (
col1 varchar(4) generated by default as identity not null,
col2 varchar(30) not null,
@@ -6,7 +7,6 @@ create table foo (
);
comment on table foo is 'comment';
-- apply alter tables
alter table foo add column added_to_foo varchar(20);
alter table foo drop column col2;
@@ -1,2 +1,3 @@
-- drop all
drop table if exists foo;
@@ -21,6 +21,7 @@ create table ckey_parent (
constraint pk_ckey_parent primary key (one_key,two_key)
);
-- foreign keys and indices
create index ix_ckey_detail_parent on ckey_detail (one_key,two_key);
alter table ckey_detail add constraint fk_ckey_detail_parent foreign key (one_key,two_key) references ckey_parent (one_key,two_key) on delete restrict on update restrict;
@@ -13,6 +13,7 @@ create table phones (
constraint pk_phones primary key (id)
);
-- foreign keys and indices
create index ix_phones_person_id on phones (person_id);
alter table phones add constraint fk_phones_person_id foreign key (person_id) references persons (id) on delete restrict on update restrict;
@@ -13,6 +13,7 @@ create table phones (
constraint pk_phones primary key (id)
);
-- foreign keys and indices
create index ix_phones_person_id on phones (person_id);
alter table phones add constraint fk_phones_person_id foreign key (person_id) references persons (id) on delete restrict on update restrict not valid;
@@ -1,3 +1,7 @@
-- drop all
drop table foo cascade;
-- apply changes
create column table foo (
col1 nvarchar(4) generated by default as identity not null,
col2 nvarchar(30) not null,
@@ -1,3 +1,7 @@
-- drop all
drop table if exists foo;
-- apply changes
create table foo (
col1 varchar(4) generated by default as identity not null,
col2 varchar(30) not null,
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>12.15.1</version>
<version>12.16.0</version>
</parent>
<name>ebean external mapping api</name>
+6 -6
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>12.15.1</version>
<version>12.16.0</version>
</parent>
<!-- <parent>-->
<!-- <groupId>org.avaje</groupId>-->
@@ -14,7 +14,7 @@
<scm>
<developerConnection>scm:git:git@github.com:ebean-orm/ebean.git</developerConnection>
<tag>ebean-parent-12.15.1</tag>
<tag>ebean-parent-12.16.0</tag>
</scm>
<name>ebean external mapping xml</name>
@@ -33,7 +33,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-externalmapping-api</artifactId>
<version>12.15.1</version>
<version>12.16.0</version>
</dependency>
<dependency>
@@ -59,21 +59,21 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>12.15.1</version>
<version>12.16.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-ddl-generator</artifactId>
<version>12.15.1</version>
<version>12.16.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.avaje</groupId>
<artifactId>junit</artifactId>
<version>1.0</version>
<version>1.1</version>
<scope>test</scope>
</dependency>
+4 -4
View File
@@ -5,7 +5,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>12.15.1-SNAPSHOT</version>
<version>12.16.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -29,7 +29,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>12.15.1-SNAPSHOT</version>
<version>12.16.0-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
@@ -44,14 +44,14 @@
<dependency>
<groupId>io.avaje</groupId>
<artifactId>junit</artifactId>
<version>1.0</version>
<version>1.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-test</artifactId>
<version>12.15.1-SNAPSHOT</version>
<version>12.16.0-SNAPSHOT</version>
<scope>test</scope>
</dependency>
+6 -13
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>12.15.1</version>
<version>12.16.0</version>
</parent>
<name>ebean postgis</name>
@@ -14,7 +14,7 @@
<artifactId>ebean-postgis</artifactId>
<properties>
<postgis.jdbc.version>2.2.1</postgis.jdbc.version>
<postgis.jdbc.version>2.5.1</postgis.jdbc.version>
</properties>
<dependencies>
@@ -23,7 +23,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>12.15.1</version>
<version>12.16.0</version>
<scope>provided</scope>
</dependency>
@@ -37,7 +37,7 @@
<dependency>
<groupId>net.postgis</groupId>
<artifactId>postgis-jdbc</artifactId>
<version>[${postgis.jdbc.version}]</version>
<version>${postgis.jdbc.version}</version>
</dependency>
<!-- expected to be provided -->
@@ -64,17 +64,10 @@
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-datasource</artifactId>
<version>${ebean-datasource.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-test</artifactId>
<version>12.15.1</version>
<version>12.16.0</version>
<scope>test</scope>
</dependency>
@@ -88,7 +81,7 @@
<dependency>
<groupId>io.avaje</groupId>
<artifactId>junit</artifactId>
<version>1.0</version>
<version>1.1</version>
<scope>test</scope>
</dependency>
@@ -7,8 +7,10 @@ import io.ebean.core.type.DataReader;
import io.ebean.core.type.DocPropertyType;
import io.ebean.core.type.ScalarType;
import org.postgis.Geometry;
import org.postgis.GeometryBuilder;
import org.postgis.PGgeometry;
import org.postgis.PGgeometryLW;
import org.postgis.binary.BinaryParser;
import org.postgresql.util.PGobject;
import java.io.DataInput;
@@ -51,7 +53,7 @@ abstract class ScalarTypePgisBase<T extends Geometry> implements ScalarType<T> {
return (T) ((PGgeometry) object).getGeometry();
} else if (object instanceof PGobject) {
return (T) PGgeometry.geomFromString(((PGobject) object).getValue());
return (T) GeometryBuilder.geomFromString(((PGobject) object).getValue(), new BinaryParser(), false);
} else {
throw new IllegalStateException("Could not convert from " + object.getClass() + " to " + cls);
+6 -6
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>12.15.1</version>
<version>12.16.0</version>
</parent>
<name>ebean querybean</name>
@@ -17,7 +17,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>12.15.1</version>
<version>12.16.0</version>
<scope>provided</scope>
</dependency>
@@ -56,28 +56,28 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-ddl-generator</artifactId>
<version>12.15.1</version>
<version>12.16.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>querybean-generator</artifactId>
<version>12.15.1</version>
<version>12.16.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-test</artifactId>
<version>12.15.1</version>
<version>12.16.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.avaje</groupId>
<artifactId>junit</artifactId>
<version>1.0</version>
<version>1.1</version>
<scope>test</scope>
</dependency>
+7 -7
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>12.15.1</version>
<version>12.16.0</version>
</parent>
<artifactId>ebean-redis</artifactId>
@@ -22,42 +22,42 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-api</artifactId>
<version>12.15.1</version>
<version>12.16.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>12.15.1</version>
<version>12.16.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>12.15.1</version>
<version>12.16.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>querybean-generator</artifactId>
<version>12.15.1</version>
<version>12.16.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-test</artifactId>
<version>12.15.1</version>
<version>12.16.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.avaje</groupId>
<artifactId>junit</artifactId>
<version>1.0</version>
<version>1.1</version>
<scope>test</scope>
</dependency>
+4 -4
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>ebean-parent</artifactId>
<groupId>io.ebean</groupId>
<version>12.15.1</version>
<version>12.16.0</version>
</parent>
<name>ebean test</name>
@@ -29,14 +29,14 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
<version>12.15.1</version>
<version>12.16.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-ddl-generator</artifactId>
<version>12.15.1</version>
<version>12.16.0</version>
</dependency>
<dependency>
@@ -233,7 +233,7 @@
<dependency>
<groupId>io.avaje</groupId>
<artifactId>junit</artifactId>
<version>1.0</version>
<version>1.1</version>
<scope>test</scope>
</dependency>
@@ -235,6 +235,9 @@ public class DbMigrationTest extends BaseTestCase {
case SQLSERVER17: // these DBs are 'standard based' so they also do not support HistoryExclude
case MARIADB:
case HANA:
case DB2LUW:
case DB2FORI: // not yet tested
case DB2ZOS: // not yet tested
assertThat(versions2.get(0).getDiff().toString()).as("using platform: %s, versions2:%s", server().platform(), versions2)
.contains("testString=foo2,foo1")
.contains("testString2=bar2,bar1");
@@ -4,17 +4,21 @@ import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.slf4j.MDC;
import io.ebean.config.MdcBackgroundExecutorWrapper;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class DefaultBackgroundExecutorTest {
@Test
public void submit_callable() throws Exception {
DefaultBackgroundExecutor es = new DefaultBackgroundExecutor(1, 2, "test");
DefaultBackgroundExecutor es = new DefaultBackgroundExecutor(1, 2, "test", null);
final Future<String> future0 = es.submit(() -> "Hello");
final Future<String> future1 = es.submit(() -> "There");
@@ -39,7 +43,7 @@ public class DefaultBackgroundExecutorTest {
public void shutdown_slowCallable_expect_interrupted() throws Exception {
int shutdownWaitSecs = 1;
DefaultBackgroundExecutor es = new DefaultBackgroundExecutor(1, shutdownWaitSecs, "test");
DefaultBackgroundExecutor es = new DefaultBackgroundExecutor(1, shutdownWaitSecs, "test", null);
final Future<String> future2 = es.submit(() -> {
try {
@@ -61,7 +65,7 @@ public class DefaultBackgroundExecutorTest {
@Disabled("test takes long time")
public void shutdown_when_running_expect_waitAndNiceShutdown() {
DefaultBackgroundExecutor es = new DefaultBackgroundExecutor(1, 20, "test");
DefaultBackgroundExecutor es = new DefaultBackgroundExecutor(1, 20, "test", null);
es.execute(new RunFor(3000, "a"));
es.execute(new RunFor(3000, "b"));
@@ -74,7 +78,7 @@ public class DefaultBackgroundExecutorTest {
@Disabled("test takes long time")
public void shutdown_when_rougeRunnable_expect_InterruptedException() {
DefaultBackgroundExecutor es = new DefaultBackgroundExecutor(1, 10, "test");
DefaultBackgroundExecutor es = new DefaultBackgroundExecutor(1, 10, "test", null);
es.execute(new RunFor(300000, "a"));
es.execute(new RunFor(3000, "b"));
@@ -85,12 +89,12 @@ public class DefaultBackgroundExecutorTest {
@Test
public void wrapWithNoMDC() {
DefaultBackgroundExecutor es = new DefaultBackgroundExecutor(1, 10, "test");
DefaultBackgroundExecutor es = new DefaultBackgroundExecutor(1, 10, "test", null);
assertThat(MDC.getCopyOfContextMap()).isNull();
es.wrapMDC(() -> {
es.wrap(() -> {
assertThat(MDC.getCopyOfContextMap()).isNull();
});
es.wrapMDC(() -> {
es.wrap(() -> {
assertThat(MDC.getCopyOfContextMap()).isNull();
return "Callable";
});
@@ -98,25 +102,44 @@ public class DefaultBackgroundExecutorTest {
}
@Test
public void wrapWithMDC_expect_() {
DefaultBackgroundExecutor es = new DefaultBackgroundExecutor(1, 10, "test");
public void wrapWithMDC_expect_() throws Exception {
DefaultBackgroundExecutor es = new DefaultBackgroundExecutor(1, 10, "test", new MdcBackgroundExecutorWrapper());
// MDC has a copyOnThread map. So we must pass different values to check if the test will work
MDC.clear();
es.submit(()->{
assertThat(MDC.get("hello")).isNull();
}).get();
MDC.put("hello", "there");
es.wrapMDC(() -> {
es.wrap(() -> {
assertThat(MDC.get("hello")).isEqualTo("there");
});
es.wrapMDC(() -> {
}).run(); // will clear the MDC. But this should be OK
MDC.put("hello", "there");
es.wrap(() -> {
assertThat(MDC.get("hello")).isEqualTo("there");
return "Callable";
});
}).call(); // will clear the MDC. But this should be OK
MDC.put("hello", "there");
CountDownLatch latch = new CountDownLatch(1);
es.execute(() -> {
// the assertion is executed async, so it will only logged on console
assertThat(MDC.get("hello")).isEqualTo("there");
latch.countDown();
});
assertTrue(latch.await(5, TimeUnit.SECONDS));
es.submit(() -> {
assertThat(MDC.get("hello")).isEqualTo("there");
return "Callable";
});
}).get();
MDC.clear();
es.execute(()->{
assertThat(MDC.get("hello")).isNull();
});
es.shutdown();
}
@@ -9,7 +9,6 @@ class BasicProfileLocationTest {
@Test
void obtain() {
DProfileLocation loc = new DTimedProfileLocation(12, "foo", MetricFactory.get().createTimedMetric("junk"));
String javaVersion = System.getProperty("java.version");
@@ -18,7 +17,7 @@ class BasicProfileLocationTest {
assertThat(loc.fullLocation()).endsWith("invoke0(Native Method:12)");
assertThat(loc.location()).isEqualTo("sun.reflect.NativeMethodAccessorImpl.invoke0");
assertThat(loc.label()).isEqualTo("NativeMethodAccessorImpl.invoke0");
} else if (javaVersion.startsWith("18")){
} else if (javaVersion.startsWith("18") || javaVersion.startsWith("19")){
assertThat(loc.fullLocation()).endsWith("jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)");
assertThat(loc.location()).isEqualTo("java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke");
assertThat(loc.label()).isEqualTo("DirectMethodHandleAccessor.invoke");
@@ -31,7 +30,6 @@ class BasicProfileLocationTest {
@Test
void basic_trimPackage() {
BasicProfileLocation loc = new BasicProfileLocation("com.foo.Bar.all");
assertThat(loc.obtain()).isFalse();
assertThat(loc.fullLocation()).isEqualTo("com.foo.Bar.all");
@@ -41,7 +39,6 @@ class BasicProfileLocationTest {
@Test
void basic_trimSinglePackage() {
BasicProfileLocation loc = new BasicProfileLocation("foo.Bar.all");
assertThat(loc.obtain()).isFalse();
assertThat(loc.fullLocation()).isEqualTo("foo.Bar.all");
@@ -2,11 +2,9 @@ package misc.migration.v1_1;
import io.ebean.annotation.DbDefault;
import io.ebean.annotation.DbMigration;
import io.ebean.annotation.History;
import io.ebean.annotation.HistoryExclude;
import io.ebean.annotation.NotNull;
import io.ebean.annotation.Platform;
import javax.persistence.Entity;
import javax.persistence.Id;
@@ -22,13 +20,6 @@ public class EHistory2 {
Integer id;
@NotNull
// see: https://mariadb.com/de/resources/blog/use-cases-for-mariadb-data-versioning/
@DbMigration(preAlter = {
"SET @@system_versioning_alter_history = 1",
"update ${table} set ${column} = 'unknown' where ${column} is null"}, platforms = Platform.MARIADB)
// other platforms
@DbMigration(preAlter = "update ${table} set ${column} = 'unknown' where ${column} is null")
@DbDefault("unknown")
String testString;
@@ -8,13 +8,14 @@ import org.junit.jupiter.api.Test;
import org.tests.model.basic.EBasicClobNoVer;
import java.util.List;
import java.util.Random;
import static org.assertj.core.api.Assertions.assertThat;
public class TestBasicClobNoVer extends BaseTestCase {
class TestBasicClobNoVer extends BaseTestCase {
@Test
public void test() {
void test() {
EBasicClobNoVer entity = new EBasicClobNoVer();
entity.setName("test");
@@ -65,4 +66,36 @@ public class TestBasicClobNoVer extends BaseTestCase {
assertThat(entity.getDescription()).isEqualTo("modified");
}
@Test
void refresh_withSoftDelete() {
EBasicClobNoVer bean = new EBasicClobNoVer();
bean.setDescription("hello");
DB.save(bean);
DB.refresh(bean);
LoggedSql.start();
bean.children().forEach(System.out::println);
List<String> sql = LoggedSql.stop();
assertThat(sql).hasSize(1);
assertThat(sql.get(0)).contains(" and t0.deleted =");
}
@Test
void largeValueInsert() {
EBasicClobNoVer bean = new EBasicClobNoVer();
bean.setDescription(largeContent());
DB.save(bean);
}
private String largeContent() {
Random random = new Random();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1048577; i++) {
sb.append((char) (random.nextInt(26) + 'a'));
}
return sb.toString();
}
}
@@ -0,0 +1,108 @@
package org.tests.cache;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import java.util.concurrent.Callable;
import org.junit.jupiter.api.Test;
import org.tests.model.basic.OCachedBean;
import io.ebean.BaseTestCase;
import io.ebean.DB;
import io.ebean.Database;
import io.ebean.DatabaseFactory;
import io.ebean.config.BackgroundExecutorWrapper;
import io.ebean.config.CurrentTenantProvider;
import io.ebean.config.DatabaseConfig;
import io.ebean.config.MdcBackgroundExecutorWrapper;
import io.ebeaninternal.server.cache.DefaultServerCachePlugin;
/**
* Test class testing async/background cache updates in a multi-tenant environment.
*/
public class TestBeanCacheAsync extends BaseTestCase {
private final ThreadLocal<String> tenantId = new ThreadLocal<>();
class ThreadLocalTenantProvider implements CurrentTenantProvider {
@Override
public Object currentId() {
return tenantId.get();
}
}
/**
* Copy tenant info to the background thread.
*/
class TenantCopyBackgroundExecutorWrapper implements BackgroundExecutorWrapper {
@Override
public <T> Callable<T> wrap(Callable<T> task) {
String tenant = tenantId.get(); // executed in current thread
return () -> {
tenantId.set(tenant); // executed in other thread
try {
return task.call();
} finally {
tenantId.remove();
}
};
}
@Override
public Runnable wrap(Runnable task) {
String tenant = tenantId.get();
return () -> {
tenantId.set(tenant);
try {
task.run();
} finally {
tenantId.remove();
}
};
}
}
@Test
public void findById_with_tenant() throws InterruptedException {
DatabaseConfig config = new DatabaseConfig();
config.setName(DB.getDefault().name());
config.loadFromProperties();
config.setDataSource(DB.getDefault().dataSource());
config.setReadOnlyDataSource(DB.getDefault().readOnlyDataSource());
config.setDdlExtra(false);
config.setDdlGenerate(false);
config.setDdlRun(false);
config.setDefaultServer(false);
config.setRegister(false);
config.setServerCachePlugin(new DefaultServerCachePlugin()); // disables foreground local caching (as it is done in Hz/Ignite)
config.setCurrentTenantProvider(new ThreadLocalTenantProvider());
config.setBackgroundExecutorWrapper(
new MdcBackgroundExecutorWrapper().with(new TenantCopyBackgroundExecutorWrapper()));
tenantId.set("4711");
Database db = DatabaseFactory.create(config);
try {
OCachedBean bean = new OCachedBean();
bean.setName("findById");
db.save(bean);
OCachedBean bean0 = db.find(OCachedBean.class, bean.getId());
assertNotNull(bean0);
assertThat(bean0.getName()).isEqualTo("findById");
bean0.setName("findById2");
db.save(bean0);
Thread.sleep(100); // TODO: can we block finds on that ID if a pending cache update is present?
bean0 = db.find(OCachedBean.class, bean.getId());
assertNotNull(bean0);
assertThat(bean0.getName()).isEqualTo("findById2");
} finally {
db.shutdown(false, false);
}
}
}
@@ -3,6 +3,9 @@ package org.tests.model.basic;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Lob;
import javax.persistence.OneToMany;
import java.util.ArrayList;
import java.util.List;
@Entity
public class EBasicClobNoVer {
@@ -18,6 +21,9 @@ public class EBasicClobNoVer {
@Lob
private String description;
@OneToMany
private List<EBasicClobNoVerChild> children = new ArrayList<>();
public void setId(Long id) {
this.id = id;
}
@@ -41,4 +47,13 @@ public class EBasicClobNoVer {
public String getDescription() {
return description;
}
public List<EBasicClobNoVerChild> children() {
return children;
}
public EBasicClobNoVer children(List<EBasicClobNoVerChild> children) {
this.children = children;
return this;
}
}
@@ -0,0 +1,49 @@
package org.tests.model.basic;
import io.ebean.annotation.SoftDelete;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
@Entity
public class EBasicClobNoVerChild {
@Id
private Long id;
@ManyToOne
private final EBasicClobNoVer parent;
private final String child;
@SoftDelete
private boolean deleted;
public EBasicClobNoVerChild(EBasicClobNoVer parent, String child) {
this.parent = parent;
this.child = child;
}
public Long id() {
return id;
}
public EBasicClobNoVerChild id(Long id) {
this.id = id;
return this;
}
public String child() {
return child;
}
public boolean deleted() {
return deleted;
}
public EBasicClobNoVerChild deleted(boolean deleted) {
this.deleted = deleted;
return this;
}
}
@@ -9,10 +9,10 @@ import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
public class TestMTOInheritNoDiscriminator extends BaseTestCase {
class TestMTOInheritNoDiscriminator extends BaseTestCase {
@Test
public void test() {
void test() {
final TTruckHolder holder = setup();
@@ -31,7 +31,7 @@ public class TestMTOInheritNoDiscriminator extends BaseTestCase {
assertThat(sql).hasSize(1);
if (isH2() || isPostgresCompatible()) {
assertSql(sql.get(0)).contains("select t0.id, t0.name, t0.version, t2.type, t0.truck_plate_no, t0.basic_id, t1.id, t1.some_uid, t1.foo, t1.owner_id from ttruck_holder t0 join tcar t2 on t2.plate_no = t0.truck_plate_no left join ttruck_holder_item t1 on t1.owner_id = t0.id where t0.id = ? order by t0.id");
assertSql(sql.get(0)).contains("select t0.id, t0.name, t0.version, t2.type, t0.truck_plate_no, t0.basic_id, t1.id, t1.some_uid, t1.foo, t1.owner_id from ttruck_holder t0 left join ttruck_holder_item t1 on t1.owner_id = t0.id join tcar t2 on t2.plate_no = t0.truck_plate_no where t0.id = ? order by t0.id");
}
}
@@ -70,7 +70,7 @@ public class TestHistoryExclude extends BaseTestCase {
linkFound.getDocs().size();
}
@IgnorePlatform({Platform.ORACLE, Platform.DB2, Platform.COCKROACH})
@IgnorePlatform({Platform.ORACLE, Platform.COCKROACH})
@Test
public void testAsOfThenLazy() {
@@ -37,7 +37,7 @@ public class TestHistoryInclude extends BaseTestCase {
assertThat(linkFound.getDocs().size()).isEqualTo(2);
}
@IgnorePlatform({Platform.ORACLE, Platform.DB2, Platform.COCKROACH})
@IgnorePlatform({Platform.ORACLE, Platform.COCKROACH})
@Test
public void testAsOfThenLazy() {
@@ -14,7 +14,7 @@ import static org.assertj.core.api.Assertions.assertThat;
public class TestHistoryOneToMany extends BaseTestCase {
@IgnorePlatform({Platform.ORACLE, Platform.DB2, Platform.COCKROACH})
@IgnorePlatform({Platform.ORACLE, Platform.COCKROACH})
@Test
public void test() throws InterruptedException {
@@ -0,0 +1,10 @@
package org.tests.model.join;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
@DiscriminatorValue("A")
@Entity
public class AccountAccess extends HAccess {
}

Some files were not shown because too many files have changed in this diff Show More