diff --git a/.github/workflows/jdk-ea.yml b/.github/workflows/jdk-ea.yml new file mode 100644 index 000000000..94b202dd4 --- /dev/null +++ b/.github/workflows/jdk-ea.yml @@ -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 + diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..1ab0a32fa --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2012 Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/LICENSE.TXT b/LICENSE.TXT deleted file mode 100644 index d62eb869b..000000000 --- a/LICENSE.TXT +++ /dev/null @@ -1,15 +0,0 @@ -EbeanORM - -Copyright 2012 Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. \ No newline at end of file diff --git a/ebean-api/pom.xml b/ebean-api/pom.xml index 03615393d..addca9579 100644 --- a/ebean-api/pom.xml +++ b/ebean-api/pom.xml @@ -106,7 +106,7 @@ io.avaje junit - 1.0 + 1.1 test diff --git a/ebean-api/src/main/java/io/ebean/Version.java b/ebean-api/src/main/java/io/ebean/Version.java index c00aa576d..77bb17144 100644 --- a/ebean-api/src/main/java/io/ebean/Version.java +++ b/ebean-api/src/main/java/io/ebean/Version.java @@ -98,4 +98,14 @@ public class Version { public Map getDiff() { return diff; } + + @Override + public String toString() { + return "Version{" + + "bean=" + bean + + ", start=" + start + + ", end=" + end + + ", diff=" + diff + + '}'; + } } diff --git a/ebean-api/src/main/java/io/ebean/bean/EntityBeanIntercept.java b/ebean-api/src/main/java/io/ebean/bean/EntityBeanIntercept.java index 2effe73d9..86de48bd8 100644 --- a/ebean-api/src/main/java/io/ebean/bean/EntityBeanIntercept.java +++ b/ebean-api/src/main/java/io/ebean/bean/EntityBeanIntercept.java @@ -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); } diff --git a/ebean-api/src/main/java/io/ebean/config/dbplatform/DatabasePlatform.java b/ebean-api/src/main/java/io/ebean/config/dbplatform/DatabasePlatform.java index 3950411aa..276151075 100644 --- a/ebean-api/src/main/java/io/ebean/config/dbplatform/DatabasePlatform.java +++ b/ebean-api/src/main/java/io/ebean/config/dbplatform/DatabasePlatform.java @@ -606,13 +606,6 @@ public class DatabasePlatform { this.useMigrationStoredProcedures = useMigrationStoredProcedures; } - /** - * Normally not needed - overridden in CockroachPlatform. - */ - public boolean isDdlAutoCommit() { - return false; - } - /** * Return the DB identity/sequence features for this platform. * diff --git a/ebean-api/src/main/java/io/ebean/config/dbplatform/clickhouse/ClickHousePlatform.java b/ebean-api/src/main/java/io/ebean/config/dbplatform/clickhouse/ClickHousePlatform.java index f1e8eca21..bb1b141e6 100644 --- a/ebean-api/src/main/java/io/ebean/config/dbplatform/clickhouse/ClickHousePlatform.java +++ b/ebean-api/src/main/java/io/ebean/config/dbplatform/clickhouse/ClickHousePlatform.java @@ -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); diff --git a/ebean-api/src/main/java/io/ebean/config/dbplatform/cockroach/CockroachPlatform.java b/ebean-api/src/main/java/io/ebean/config/dbplatform/cockroach/CockroachPlatform.java index 8c1388885..e388f87f6 100644 --- a/ebean-api/src/main/java/io/ebean/config/dbplatform/cockroach/CockroachPlatform.java +++ b/ebean-api/src/main/java/io/ebean/config/dbplatform/cockroach/CockroachPlatform.java @@ -11,14 +11,7 @@ public class CockroachPlatform extends PostgresPlatform { public CockroachPlatform() { super(); this.platform = Platform.COCKROACH; - } - - /** - * Needs a commit after create index such that alter table add foreign key ... succeeds. - */ - @Override - public boolean isDdlAutoCommit() { - return false; + this.historySupport = null; // not yet implemented in DDL } } diff --git a/ebean-api/src/main/java/io/ebean/config/dbplatform/db2/BaseDB2Platform.java b/ebean-api/src/main/java/io/ebean/config/dbplatform/db2/BaseDB2Platform.java index 949e978c3..e84680233 100644 --- a/ebean-api/src/main/java/io/ebean/config/dbplatform/db2/BaseDB2Platform.java +++ b/ebean-api/src/main/java/io/ebean/config/dbplatform/db2/BaseDB2Platform.java @@ -37,6 +37,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)); diff --git a/ebean-api/src/main/java/io/ebean/config/dbplatform/db2/DB2HistorySupport.java b/ebean-api/src/main/java/io/ebean/config/dbplatform/db2/DB2HistorySupport.java new file mode 100644 index 000000000..122f1b57c --- /dev/null +++ b/ebean-api/src/main/java/io/ebean/config/dbplatform/db2/DB2HistorySupport.java @@ -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"; + } + +} diff --git a/ebean-api/src/main/java/io/ebean/config/dbplatform/h2/H2HistoryTrigger.java b/ebean-api/src/main/java/io/ebean/config/dbplatform/h2/H2HistoryTrigger.java index 2867ca06c..d6c74df1f 100644 --- a/ebean-api/src/main/java/io/ebean/config/dbplatform/h2/H2HistoryTrigger.java +++ b/ebean-api/src/main/java/io/ebean/config/dbplatform/h2/H2HistoryTrigger.java @@ -5,6 +5,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.sql.*; +import java.time.LocalDateTime; import java.util.Arrays; /** @@ -76,7 +77,7 @@ public class H2HistoryTrigger implements Trigger { public void fire(Connection connection, Object[] oldRow, Object[] newRow) throws SQLException { if (oldRow != null) { // a delete or update event - Timestamp now = new Timestamp(System.currentTimeMillis()); + LocalDateTime now = LocalDateTime.now(); oldRow[effectEndPosition] = now; if (newRow != null) { // update event. Set the effective start timestamp to now. diff --git a/ebean-api/src/main/java/io/ebean/config/dbplatform/hana/HanaPlatform.java b/ebean-api/src/main/java/io/ebean/config/dbplatform/hana/HanaPlatform.java index 5fb91fcc8..f7f4c62e2 100644 --- a/ebean-api/src/main/java/io/ebean/config/dbplatform/hana/HanaPlatform.java +++ b/ebean-api/src/main/java/io/ebean/config/dbplatform/hana/HanaPlatform.java @@ -92,4 +92,8 @@ public class HanaPlatform extends DatabasePlatform { } } + @Override + public boolean isUseMigrationStoredProcedures() { + return true; + } } diff --git a/ebean-api/src/main/java/io/ebean/plugin/DeployBeanDescriptorMeta.java b/ebean-api/src/main/java/io/ebean/plugin/DeployBeanDescriptorMeta.java index a5afba570..4347a5057 100644 --- a/ebean-api/src/main/java/io/ebean/plugin/DeployBeanDescriptorMeta.java +++ b/ebean-api/src/main/java/io/ebean/plugin/DeployBeanDescriptorMeta.java @@ -1,7 +1,6 @@ package io.ebean.plugin; import java.util.Collection; -import java.util.List; /** * General deployment information. This is used in {@link CustomDeployParser}. diff --git a/ebean-api/src/main/java/io/ebean/plugin/DeployBeanPropertyMeta.java b/ebean-api/src/main/java/io/ebean/plugin/DeployBeanPropertyMeta.java index b581181d2..4ba2d3e2a 100644 --- a/ebean-api/src/main/java/io/ebean/plugin/DeployBeanPropertyMeta.java +++ b/ebean-api/src/main/java/io/ebean/plugin/DeployBeanPropertyMeta.java @@ -1,7 +1,6 @@ package io.ebean.plugin; import java.lang.reflect.Field; -import java.lang.reflect.Method; public interface DeployBeanPropertyMeta { diff --git a/ebean-api/src/test/java/io/ebean/util/TestAnnotationUtil.java b/ebean-api/src/test/java/io/ebean/util/TestAnnotationUtil.java index 7d7519c03..f75433c09 100644 --- a/ebean-api/src/test/java/io/ebean/util/TestAnnotationUtil.java +++ b/ebean-api/src/test/java/io/ebean/util/TestAnnotationUtil.java @@ -2,7 +2,6 @@ package io.ebean.util; import static org.assertj.core.api.Assertions.assertThat; -import java.io.File; import java.util.Set; import org.junit.jupiter.api.Test; diff --git a/ebean-autotune/pom.xml b/ebean-autotune/pom.xml index c76dd6c4e..5e5bbc303 100644 --- a/ebean-autotune/pom.xml +++ b/ebean-autotune/pom.xml @@ -48,7 +48,7 @@ io.avaje junit - 1.0 + 1.1 test diff --git a/ebean-core/pom.xml b/ebean-core/pom.xml index 976d01267..0fd3a466c 100644 --- a/ebean-core/pom.xml +++ b/ebean-core/pom.xml @@ -23,7 +23,7 @@ io.ebean ebean-ddl-runner - 1.2 + ${ebean-ddl-runner.version} @@ -130,7 +130,7 @@ org.postgresql postgresql - 42.3.2 + 42.3.3 true @@ -146,7 +146,7 @@ io.avaje junit - 1.0 + 1.1 test diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/SpiEbeanServer.java b/ebean-core/src/main/java/io/ebeaninternal/api/SpiEbeanServer.java index 53d0c4551..027956e1f 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/api/SpiEbeanServer.java +++ b/ebean-core/src/main/java/io/ebeaninternal/api/SpiEbeanServer.java @@ -3,8 +3,6 @@ package io.ebeaninternal.api; import io.ebean.*; import io.ebean.bean.BeanCollectionLoader; import io.ebean.bean.CallOrigin; -import io.ebean.config.DatabaseConfig; -import io.ebean.config.dbplatform.DatabasePlatform; import io.ebean.event.readaudit.ReadAuditLogger; import io.ebean.event.readaudit.ReadAuditPrepare; import io.ebean.meta.MetricVisitor; diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/SpiQueryBindCapture.java b/ebean-core/src/main/java/io/ebeaninternal/api/SpiQueryBindCapture.java index 636b6689c..db5cbd9b6 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/api/SpiQueryBindCapture.java +++ b/ebean-core/src/main/java/io/ebeaninternal/api/SpiQueryBindCapture.java @@ -1,6 +1,5 @@ package io.ebeaninternal.api; -import io.ebean.meta.QueryPlanRequest; import io.ebeaninternal.server.type.bindcapture.BindCapture; /** diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/changelog/ChangeJsonBuilder.java b/ebean-core/src/main/java/io/ebeaninternal/server/changelog/ChangeJsonBuilder.java index f399b78ad..5d71023b8 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/changelog/ChangeJsonBuilder.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/changelog/ChangeJsonBuilder.java @@ -5,7 +5,6 @@ import com.fasterxml.jackson.core.JsonGenerator; import io.ebean.event.changelog.BeanChange; import io.ebean.event.changelog.ChangeSet; import io.ebean.event.changelog.ChangeType; -import io.ebean.text.json.JsonContext; import java.io.IOException; import java.io.Writer; diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/core/AbstractSqlQueryRequest.java b/ebean-core/src/main/java/io/ebeaninternal/server/core/AbstractSqlQueryRequest.java index f11cf9f8e..01812fe5f 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/core/AbstractSqlQueryRequest.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/core/AbstractSqlQueryRequest.java @@ -15,8 +15,6 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.util.concurrent.locks.ReentrantLock; -import javax.persistence.PersistenceException; - /** * Wraps the objects involved in executing a SQL / Relational Query. */ diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/core/ClassPathScanners.java b/ebean-core/src/main/java/io/ebeaninternal/server/core/ClassPathScanners.java index ff41de9b4..8810465b2 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/core/ClassPathScanners.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/core/ClassPathScanners.java @@ -5,7 +5,6 @@ import io.avaje.classpath.scanner.ClassPathScannerFactory; import io.ebean.config.DatabaseConfig; import java.util.ArrayList; -import java.util.Iterator; import java.util.List; import java.util.ServiceLoader; diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/core/DScriptRunner.java b/ebean-core/src/main/java/io/ebeaninternal/server/core/DScriptRunner.java index fdb24bd0d..06916912c 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/core/DScriptRunner.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/core/DScriptRunner.java @@ -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); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultBeanLoader.java b/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultBeanLoader.java index 9de9ab7ff..854842b21 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultBeanLoader.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultBeanLoader.java @@ -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; @@ -40,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()); @@ -106,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()) { @@ -200,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) { diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java index 2e4247bf8..3e5ae47b3 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java @@ -992,7 +992,7 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty { */ public String renderDbType(DbPlatformType dbType, boolean strict) { if (dbColumnDefn != null) { - if(dbColumnDefn.endsWith(";")) { + if (dbColumnDefn.endsWith(";")) { return dbColumnDefn + dbType.renderType(dbLength, dbScale, strict); } return dbColumnDefn; @@ -1165,7 +1165,7 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty { } /** - * Returns true if this isLob() or the type will effectively map to a lob. + * Returns true if this isLob() or the type will effectively map to a lob. */ @Override public boolean isDbLob() { @@ -1173,17 +1173,17 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty { return true; } switch (dbType) { - case DbPlatformType.JSON: - case DbPlatformType.JSONB: - return dbLength == 0; // must be analog to DbPlatformTypeMapping.lookup - case DbPlatformType.JSONBlob: - case DbPlatformType.JSONClob: - return true; - default: - return false; + case DbPlatformType.JSON: + case DbPlatformType.JSONB: + return dbLength == 0; // must be analog to DbPlatformTypeMapping.lookup + case DbPlatformType.JSONBlob: + case DbPlatformType.JSONClob: + return true; + default: + return false; } } - + public static boolean isLobType(int type) { switch (type) { case Types.CLOB: diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationBase.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationBase.java index 4cf692468..b74035dc0 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationBase.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationBase.java @@ -1,14 +1,9 @@ package io.ebeaninternal.server.deploy.parse; -import io.ebean.annotation.Aggregation; -import io.ebean.annotation.Avg; import io.ebean.annotation.DbMigration; import io.ebean.annotation.Index; import io.ebean.annotation.Indices; -import io.ebean.annotation.Max; -import io.ebean.annotation.Min; import io.ebean.annotation.Platform; -import io.ebean.annotation.Sum; import io.ebean.config.NamingConvention; import io.ebean.config.dbplatform.DatabasePlatform; import io.ebean.util.AnnotationUtil; @@ -20,7 +15,6 @@ import javax.persistence.JoinColumns; import javax.persistence.NamedQueries; import javax.persistence.NamedQuery; import java.lang.annotation.Annotation; -import java.lang.reflect.Field; import java.util.Collections; import java.util.Set; diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/loadcontext/DLoadBeanContext.java b/ebean-core/src/main/java/io/ebeaninternal/server/loadcontext/DLoadBeanContext.java index a8533956c..5f1e37613 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/loadcontext/DLoadBeanContext.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/loadcontext/DLoadBeanContext.java @@ -67,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); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryIteratorSimple.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryIteratorSimple.java index 6d183b936..1aa5fc7ef 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryIteratorSimple.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryIteratorSimple.java @@ -3,7 +3,6 @@ package io.ebeaninternal.server.query; import io.ebean.QueryIterator; import io.ebeaninternal.server.core.OrmQueryRequest; -import javax.persistence.PersistenceException; import java.sql.SQLException; /** diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryIteratorWithBuffer.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryIteratorWithBuffer.java index 3df436716..aae6b0c47 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryIteratorWithBuffer.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryIteratorWithBuffer.java @@ -3,7 +3,6 @@ package io.ebeaninternal.server.query; import io.ebean.QueryIterator; import io.ebeaninternal.server.core.OrmQueryRequest; -import javax.persistence.PersistenceException; import java.sql.SQLException; import java.util.ArrayList; import java.util.NoSuchElementException; diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTree.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTree.java index 5b11be657..d055b94c9 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTree.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTree.java @@ -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; diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeAlias.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeAlias.java index 9d46d07c3..a3661e816 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeAlias.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeAlias.java @@ -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 joinProps = new TreeSet<>(); - private HashSet embeddedPropertyJoins; - private final TreeSet manyWhereJoinProps = new TreeSet<>(); - private final HashMap aliasMap = new HashMap<>(); - private final HashMap manyWhereAliasMap = new HashMap<>(); - private final String rootTableAlias; + private int counter; + private int manyWhereCounter; SqlTreeAlias(String rootTableAlias, SpiQuery.TemporalMode temporalMode) { this.rootTableAlias = rootTableAlias; diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeBuilder.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeBuilder.java index 0e4f11079..c0142ddbc 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeBuilder.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeBuilder.java @@ -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 joinList) { + private SqlTreeNode buildSelectChain(String prefix, STreePropertyAssoc prop, STreeType desc, List joinList) { List myJoinList = new ArrayList<>(); List extraProps = new ArrayList<>(); for (STreePropertyAssocOne one : desc.propsOne()) { @@ -256,7 +255,7 @@ public final class SqlTreeBuilder { } addManyWhereJoins(myJoinList); } - extraProps.forEach(props::add); //addExtra + extraProps.forEach(props::addExtra); if (!rawSql && manyWhereJoins.isFormulaWithJoin(prefix)) { for (String property : manyWhereJoins.getFormulaJoinProperties(prefix)) { diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeExtraJoin.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeExtraJoin.java index 42d2395b5..5547d1c92 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeExtraJoin.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeExtraJoin.java @@ -1,11 +1,8 @@ package io.ebeaninternal.server.query; -import io.ebean.bean.EntityBean; -import io.ebean.core.type.ScalarType; import io.ebean.util.SplitName; import io.ebeaninternal.api.SpiQuery; import io.ebeaninternal.server.deploy.BeanPropertyAssocOne; -import io.ebeaninternal.server.deploy.DbReadContext; import io.ebeaninternal.server.deploy.DbSqlContext; import io.ebeaninternal.server.deploy.TableJoin; diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeFormulaWhereJoin.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeFormulaWhereJoin.java index 502c28a6d..02f76b61c 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeFormulaWhereJoin.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeFormulaWhereJoin.java @@ -1,9 +1,6 @@ package io.ebeaninternal.server.query; -import io.ebean.bean.EntityBean; -import io.ebean.core.type.ScalarType; import io.ebeaninternal.api.SpiQuery; -import io.ebeaninternal.server.deploy.DbReadContext; import io.ebeaninternal.server.deploy.DbSqlContext; import java.util.List; diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeProperties.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeProperties.java index 623b1dd36..806af5ef6 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeProperties.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeProperties.java @@ -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()); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/transaction/DefaultProfileHandler.java b/ebean-core/src/main/java/io/ebeaninternal/server/transaction/DefaultProfileHandler.java index 8ac394a8c..16c4aafad 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/transaction/DefaultProfileHandler.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/transaction/DefaultProfileHandler.java @@ -10,7 +10,6 @@ import io.ebeaninternal.api.SpiProfileHandler; import org.slf4j.Logger; import java.io.*; -import java.nio.charset.StandardCharsets; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/transaction/JdbcTransaction.java b/ebean-core/src/main/java/io/ebeaninternal/server/transaction/JdbcTransaction.java index 7fb1a54e5..71201629a 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/transaction/JdbcTransaction.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/transaction/JdbcTransaction.java @@ -15,7 +15,6 @@ import io.ebeaninternal.server.persist.BatchedSqlException; import io.ebeaninternal.server.util.Str; import io.ebeanservice.docstore.api.DocStoreTransaction; import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import javax.persistence.PersistenceException; import javax.persistence.RollbackException; diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/DefaultTypeManager.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/DefaultTypeManager.java index 2337b193f..9adcf5224 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/type/DefaultTypeManager.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/DefaultTypeManager.java @@ -2,7 +2,6 @@ package io.ebeaninternal.server.type; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; -import io.ebean.annotation.DbArray; import io.ebean.annotation.DbEnumType; import io.ebean.annotation.DbEnumValue; import io.ebean.annotation.EnumValue; @@ -48,7 +47,6 @@ import java.sql.Timestamp; import java.sql.Types; import java.time.*; import java.util.*; -import java.util.Map.Entry; import java.util.concurrent.ConcurrentHashMap; /** diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeClob.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeClob.java index 7903a76ea..33a962726 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeClob.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeClob.java @@ -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(); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeTimestamp.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeTimestamp.java index f6aa01313..c7c9364e8 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeTimestamp.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeTimestamp.java @@ -10,8 +10,6 @@ import java.sql.Timestamp; import java.sql.Types; import java.time.Instant; -import static io.ebeaninternal.server.type.IsoJsonDateTimeParser.formatIso; - /** * ScalarType for java.sql.Timestamp. */ diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/TypeManager.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/TypeManager.java index 079782972..746dce154 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/type/TypeManager.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/TypeManager.java @@ -1,6 +1,5 @@ package io.ebeaninternal.server.type; -import io.ebean.annotation.DbArray; import io.ebean.core.type.ScalarType; import io.ebeaninternal.server.deploy.meta.DeployBeanProperty; diff --git a/ebean-ddl-generator/pom.xml b/ebean-ddl-generator/pom.xml index 097c717de..01e96e191 100644 --- a/ebean-ddl-generator/pom.xml +++ b/ebean-ddl-generator/pom.xml @@ -16,7 +16,7 @@ io.ebean ebean-ddl-runner - 1.2 + ${ebean-ddl-runner.version} @@ -51,7 +51,7 @@ io.avaje junit - 1.0 + 1.1 test diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/DdlPlugin.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/DdlPlugin.java index 55a4b5bad..8cff90a38 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/DdlPlugin.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/DdlPlugin.java @@ -45,7 +45,6 @@ public class DdlPlugin implements Plugin { private boolean extraDdl; private boolean createOnly; private boolean jaxbPresent; - private boolean ddlAutoCommit; private String dbSchema; private ScriptTransform scriptTransform; private Platform platform; @@ -71,7 +70,6 @@ public class DdlPlugin implements Plugin { final DatabasePlatform databasePlatform = server.databasePlatform(); this.platform = databasePlatform.getPlatform(); this.platformName = platform.base().name(); - this.ddlAutoCommit = databasePlatform.isDdlAutoCommit(); this.useMigrationStoredProcedures = config.getDatabasePlatform().isUseMigrationStoredProcedures(); this.scriptTransform = createScriptTransform(config); this.baseDir = initBaseDir(); @@ -161,11 +159,11 @@ public class DdlPlugin implements Plugin { DdlRunner runner = createDdlRunner(expectErrors, scriptName); try { - if (expectErrors || ddlAutoCommit) { + if (expectErrors) { connection.setAutoCommit(true); } runner.runAll(scriptTransform.transform(content), connection); - if (expectErrors || ddlAutoCommit) { + if (expectErrors) { connection.setAutoCommit(false); } connection.commit(); @@ -300,21 +298,13 @@ public class DdlPlugin implements Plugin { } protected String generateDropAllDdl() { - try { - dropAllContent = currentModel().getDropAllDdl(); - return dropAllContent; - } catch (IOException e) { - throw new RuntimeException(e); - } + dropAllContent = currentModel().getDropAllDdl(); + return dropAllContent; } protected String generateCreateAllDdl() { - try { - createAllContent = currentModel().getCreateDdl(); - return createAllContent; - } catch (IOException e) { - throw new RuntimeException(e); - } + createAllContent = currentModel().getCreateDdl(); + return createAllContent; } protected String getDropFileName() { diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/DefaultDbMigration.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/DefaultDbMigration.java index 76316002e..6774294ce 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/DefaultDbMigration.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/DefaultDbMigration.java @@ -17,7 +17,6 @@ import io.avaje.classpath.scanner.core.Location; import io.ebean.DB; import io.ebean.Database; import io.ebean.annotation.Platform; -import io.ebean.config.ClassLoadConfig; import io.ebean.config.DatabaseConfig; import io.ebean.config.DbConstraintNaming; import io.ebean.config.PlatformConfig; @@ -658,9 +657,9 @@ public class DefaultDbMigration implements DbMigration { // writer needs the current model to provide table/column details for // history ddl generation (triggers, history tables etc) DdlOptions options = new DdlOptions(addForeignKeySkipCheck); - DdlWrite write = new DdlWrite(new MConfiguration(), request.current, options); - PlatformDdlWriter writer = createDdlWriter(databasePlatform); - writer.processMigration(dbMigration, write, request.migrationDir, fullVersion); + DdlWrite writer = new DdlWrite(new MConfiguration(), request.current, options); + PlatformDdlWriter platformWriter = createDdlWriter(databasePlatform); + platformWriter.processMigration(dbMigration, writer, request.migrationDir, fullVersion); } return fullVersion; } @@ -727,7 +726,7 @@ public class DefaultDbMigration implements DbMigration { private void writeExtraPlatformDdl(String fullVersion, CurrentModel currentModel, Migration dbMigration, File writePath, boolean clear) throws IOException { DdlOptions options = new DdlOptions(addForeignKeySkipCheck); for (Pair pair : platforms) { - DdlWrite platformBuffer = new DdlWrite(new MConfiguration(), currentModel.read(), options); + DdlWrite writer = new DdlWrite(new MConfiguration(), currentModel.read(), options); PlatformDdlWriter platformWriter = createDdlWriter(pair.platform); File subPath = platformWriter.subPath(writePath, pair.prefix); if (clear) { @@ -735,7 +734,7 @@ public class DefaultDbMigration implements DbMigration { existing.delete(); } } - platformWriter.processMigration(dbMigration, platformBuffer, subPath, fullVersion); + platformWriter.processMigration(dbMigration, writer, subPath, fullVersion); } } diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/UnknownResourcePathException.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/UnknownResourcePathException.java index c8b29872a..ed8d65987 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/UnknownResourcePathException.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/UnknownResourcePathException.java @@ -7,6 +7,8 @@ package io.ebeaninternal.dbmigration; */ public class UnknownResourcePathException extends RuntimeException { + private static final long serialVersionUID = 8533769929372106003L; + public UnknownResourcePathException(String msg) { super(msg); } diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/BaseDdlHandler.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/BaseDdlHandler.java index f84c19cda..2148b5649 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/BaseDdlHandler.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/BaseDdlHandler.java @@ -18,7 +18,6 @@ import io.ebeaninternal.dbmigration.migration.DropHistoryTable; import io.ebeaninternal.dbmigration.migration.DropIndex; import io.ebeaninternal.dbmigration.migration.DropTable; -import java.io.IOException; import java.util.List; /** @@ -37,7 +36,7 @@ public class BaseDdlHandler implements DdlHandler { } @Override - public void generate(DdlWrite writer, ChangeSet changeSet) throws IOException { + public void generate(DdlWrite writer, ChangeSet changeSet) { List changeSetChildren = changeSet.getChangeSetChildren(); for (Object change : changeSetChildren) { @@ -79,77 +78,77 @@ public class BaseDdlHandler implements DdlHandler { } @Override - public void generateProlog(DdlWrite write) throws IOException { - tableDdl.generateProlog(write); + public void generateProlog(DdlWrite writer) { + tableDdl.generateProlog(writer); } @Override - public void generateEpilog(DdlWrite write) throws IOException { - tableDdl.generateEpilog(write); + public void generateEpilog(DdlWrite writer) { + tableDdl.generateEpilog(writer); } @Override - public void generate(DdlWrite writer, CreateTable createTable) throws IOException { + public void generate(DdlWrite writer, CreateTable createTable) { tableDdl.generate(writer, createTable); } @Override - public void generate(DdlWrite writer, DropTable dropTable) throws IOException { + public void generate(DdlWrite writer, DropTable dropTable) { tableDdl.generate(writer, dropTable); } @Override - public void generate(DdlWrite writer, AlterTable alterTable) throws IOException { + public void generate(DdlWrite writer, AlterTable alterTable) { tableDdl.generate(writer, alterTable); } @Override - public void generate(DdlWrite writer, AddTableComment addTableComment) throws IOException { + public void generate(DdlWrite writer, AddTableComment addTableComment) { tableDdl.generate(writer, addTableComment); } @Override - public void generate(DdlWrite writer, AddColumn addColumn) throws IOException { + public void generate(DdlWrite writer, AddColumn addColumn) { tableDdl.generate(writer, addColumn); } @Override - public void generate(DdlWrite writer, DropColumn dropColumn) throws IOException { + public void generate(DdlWrite writer, DropColumn dropColumn) { tableDdl.generate(writer, dropColumn); } @Override - public void generate(DdlWrite writer, AlterColumn alterColumn) throws IOException { + public void generate(DdlWrite writer, AlterColumn alterColumn) { tableDdl.generate(writer, alterColumn); } @Override - public void generate(DdlWrite writer, AddHistoryTable addHistoryTable) throws IOException { + public void generate(DdlWrite writer, AddHistoryTable addHistoryTable) { tableDdl.generate(writer, addHistoryTable); } @Override - public void generate(DdlWrite writer, DropHistoryTable dropHistoryTable) throws IOException { + public void generate(DdlWrite writer, DropHistoryTable dropHistoryTable) { tableDdl.generate(writer, dropHistoryTable); } @Override - public void generate(DdlWrite writer, CreateIndex createIndex) throws IOException { + public void generate(DdlWrite writer, CreateIndex createIndex) { tableDdl.generate(writer, createIndex); } @Override - public void generate(DdlWrite writer, DropIndex dropIndex) throws IOException { + public void generate(DdlWrite writer, DropIndex dropIndex) { tableDdl.generate(writer, dropIndex); } @Override - public void generate(DdlWrite writer, AddUniqueConstraint constraint) throws IOException { + public void generate(DdlWrite writer, AddUniqueConstraint constraint) { tableDdl.generate(writer, constraint); } @Override - public void generate(DdlWrite writer, AlterForeignKey alterForeignKey) throws IOException { + public void generate(DdlWrite writer, AlterForeignKey alterForeignKey) { tableDdl.generate(writer, alterForeignKey); } } diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/ColumnDdl.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/ColumnDdl.java index fa334e88a..e4189997f 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/ColumnDdl.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/ColumnDdl.java @@ -4,8 +4,6 @@ import io.ebeaninternal.dbmigration.migration.AddColumn; import io.ebeaninternal.dbmigration.migration.AlterColumn; import io.ebeaninternal.dbmigration.migration.DropColumn; -import java.io.IOException; - /** * Write DDL for AddColumn , DropColumn or AlterColumn. */ @@ -14,15 +12,15 @@ public interface ColumnDdl { /** * Write the add column change. */ - void generate(DdlWrite writer, AddColumn addColumn) throws IOException; + void generate(DdlWrite writer, AddColumn addColumn); /** * Write the drop column change. */ - void generate(DdlWrite writer, DropColumn dropColumn) throws IOException; + void generate(DdlWrite writer, DropColumn dropColumn); /** * Write the alter column changes. */ - void generate(DdlWrite writer, AlterColumn alterColumn) throws IOException; + void generate(DdlWrite writer, AlterColumn alterColumn); } diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/DdlAlterTable.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/DdlAlterTable.java new file mode 100644 index 000000000..c59666599 --- /dev/null +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/DdlAlterTable.java @@ -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 target + */ + void write(Appendable target) throws IOException; + + /** + * Adds an alter table command for given column. When you invoke
+ * alterTable(writer, "my_table").add("alter column","my_column).append("type integer") the resulting DDL (in + * standard implementation) would be
+ * alter table my_table alter column my_column type integer + * + * @return a DdlBuffer, which can be used for further appends. Note you MUST NOT call .endOfStatement() 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(); + + +} diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/DdlBuffer.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/DdlBuffer.java index 5d37cb762..e1c7365ea 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/DdlBuffer.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/DdlBuffer.java @@ -1,19 +1,10 @@ package io.ebeaninternal.dbmigration.ddlgeneration; -import io.ebeaninternal.dbmigration.model.MConfiguration; - -import java.io.IOException; - /** * Buffer to append generated DDL to. */ public interface DdlBuffer { - /** - * Return the configuration (default tablespaces etc). - */ - MConfiguration getConfiguration(); - /** * Return true if the buffer is empty. */ @@ -22,37 +13,37 @@ public interface DdlBuffer { /** * Append a statement allowing for null or empty statements. */ - DdlBuffer appendStatement(String content) throws IOException; + DdlBuffer appendStatement(String content); /** * Append DDL content to the buffer. */ - DdlBuffer append(String content) throws IOException; + DdlBuffer append(String content); /** * Append DDL content to the buffer with space padding. */ - DdlBuffer append(String type, int space) throws IOException; + DdlBuffer append(String type, int space); /** * Append a value that is potentially null or empty and proceed it with a space if so. */ - DdlBuffer appendWithSpace(String foreignKeyRestrict) throws IOException; + DdlBuffer appendWithSpace(String foreignKeyRestrict); /** * Append new line character to the buffer. */ - DdlBuffer newLine() throws IOException; + DdlBuffer newLine(); /** * Append the end of statement content. */ - DdlBuffer endOfStatement() throws IOException; + DdlBuffer endOfStatement(); /** * End of a change - add some whitespace. */ - DdlBuffer end() throws IOException; + DdlBuffer end(); /** * Return the buffer content. diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/DdlHandler.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/DdlHandler.java index 738e9a1dc..1090a8a24 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/DdlHandler.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/DdlHandler.java @@ -15,42 +15,40 @@ import io.ebeaninternal.dbmigration.migration.DropHistoryTable; import io.ebeaninternal.dbmigration.migration.DropIndex; import io.ebeaninternal.dbmigration.migration.DropTable; -import java.io.IOException; - /** * DDL generation interface. */ public interface DdlHandler { - void generate(DdlWrite writer, ChangeSet changeSet) throws IOException; + void generate(DdlWrite writer, ChangeSet changeSet); - void generate(DdlWrite writer, CreateTable createTable) throws IOException; + void generate(DdlWrite writer, CreateTable createTable); - void generate(DdlWrite writer, DropTable dropTable) throws IOException; + void generate(DdlWrite writer, DropTable dropTable); - void generate(DdlWrite writer, AlterTable dropTable) throws IOException; + void generate(DdlWrite writer, AlterTable dropTable); - void generate(DdlWrite writer, AddTableComment addTableComment) throws IOException; + void generate(DdlWrite writer, AddTableComment addTableComment); - void generate(DdlWrite writer, AddColumn addColumn) throws IOException; + void generate(DdlWrite writer, AddColumn addColumn); - void generate(DdlWrite writer, DropColumn dropColumn) throws IOException; + void generate(DdlWrite writer, DropColumn dropColumn); - void generate(DdlWrite writer, AlterColumn alterColumn) throws IOException; + void generate(DdlWrite writer, AlterColumn alterColumn); - void generate(DdlWrite writer, AddHistoryTable addHistoryTable) throws IOException; + void generate(DdlWrite writer, AddHistoryTable addHistoryTable); - void generate(DdlWrite writer, DropHistoryTable dropHistoryTable) throws IOException; + void generate(DdlWrite writer, DropHistoryTable dropHistoryTable); - void generate(DdlWrite writer, CreateIndex createIndex) throws IOException; + void generate(DdlWrite writer, CreateIndex createIndex); - void generate(DdlWrite writer, DropIndex dropIndex) throws IOException; + void generate(DdlWrite writer, DropIndex dropIndex); - void generate(DdlWrite writer, AddUniqueConstraint constraint) throws IOException; + void generate(DdlWrite writer, AddUniqueConstraint constraint); - void generate(DdlWrite writer, AlterForeignKey alterForeignKey) throws IOException; + void generate(DdlWrite writer, AlterForeignKey alterForeignKey); - void generateProlog(DdlWrite write) throws IOException; + void generateProlog(DdlWrite writer); - void generateEpilog(DdlWrite write) throws IOException; + void generateEpilog(DdlWrite writer); } diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/DdlWrite.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/DdlWrite.java index 0f37f3374..fc127fabd 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/DdlWrite.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/DdlWrite.java @@ -1,30 +1,46 @@ 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: + *
    + *
  • applyDropDependencies: Contains drops for foreign keys, indices, constraint or drop history table
  • + *
  • apply: Contains @DbMigraion.before, create table, create sequence or disable system versioning statements
  • + *
  • applyAlterTables: Contains table alters (only that change the table data structure, there may be table alters like + * constraints etc. in postAlter)
  • + *
  • applyPostAlter: Contains check constraints, unique constraints (which CAN be an index), column and table comments, + * @DbMigraion.after, drop tables, drop sequences or enable system versioning statement
  • + *
  • applyForeignKeys: Contains foreign keys and indices. + *
  • applyHistoryView: The views for trigger based history support
  • + *
  • applyHistoryTrigger: The triggers for trigger based history support
  • + *
*/ public class DdlWrite { private final ModelContainer currentModel; - private final DdlBuffer applyDropDependencies; + private final DdlBuffer applyDropDependencies = new BaseDdlBuffer(); - private final DdlBuffer apply; + private final DdlBuffer apply = new BaseDdlBuffer(); - private final DdlBuffer applyForeignKeys; + private final Map applyAlterTables = new TreeMap<>(); - private final DdlBuffer applyHistoryView; + private final DdlBuffer applyPostAlter = new BaseDdlBuffer(); - private final DdlBuffer applyHistoryTrigger; + private final DdlBuffer applyForeignKeys = new BaseDdlBuffer(); - private final DdlBuffer dropAllForeignKeys; + private final DdlBuffer dropAllForeignKeys = new BaseDdlBuffer(); - private final DdlBuffer dropAll; + private final DdlBuffer dropAll = new BaseDdlBuffer(); private final DdlOptions options; @@ -40,13 +56,6 @@ public class DdlWrite { */ public DdlWrite(MConfiguration configuration, ModelContainer currentModel, DdlOptions options) { this.currentModel = currentModel; - this.applyDropDependencies = new BaseDdlBuffer(configuration); - this.apply = new BaseDdlBuffer(configuration); - this.applyForeignKeys = new BaseDdlBuffer(configuration); - this.applyHistoryView = new BaseDdlBuffer(configuration); - this.applyHistoryTrigger = new BaseDdlBuffer(configuration); - this.dropAllForeignKeys = new BaseDdlBuffer(configuration); - this.dropAll = new BaseDdlBuffer(configuration); this.options = options; } @@ -73,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. */ @@ -93,6 +109,20 @@ public class DdlWrite { return applyDropDependencies; } + /** + * Creates or returns the DdlAlterTable statement for tablename. + * + * 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 factory) { + return applyAlterTables.computeIfAbsent(tableName, factory); + } + /** * Return the buffer that APPLY DDL is written to for foreign keys and their associated indexes. *

@@ -103,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. */ @@ -131,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(); + } + } diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/TableDdl.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/TableDdl.java index f88546b62..48143afe6 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/TableDdl.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/TableDdl.java @@ -14,8 +14,6 @@ import io.ebeaninternal.dbmigration.migration.DropHistoryTable; import io.ebeaninternal.dbmigration.migration.DropIndex; import io.ebeaninternal.dbmigration.migration.DropTable; -import java.io.IOException; - /** * Write table DDL. */ @@ -24,75 +22,75 @@ public interface TableDdl { /** * Generate the create table change. */ - void generate(DdlWrite writer, CreateTable createTable) throws IOException; + void generate(DdlWrite writer, CreateTable createTable); /** * Write the drop column change. */ - void generate(DdlWrite writer, DropTable dropTable) throws IOException; + void generate(DdlWrite writer, DropTable dropTable); /** * Write alter table changes. */ - void generate(DdlWrite writer, AlterTable dropTable) throws IOException; + void generate(DdlWrite writer, AlterTable dropTable); /** * Write the add column change. */ - void generate(DdlWrite writer, AddColumn addColumn) throws IOException; + void generate(DdlWrite writer, AddColumn addColumn); /** * Write the alter column changes. */ - void generate(DdlWrite writer, AlterColumn alterColumn) throws IOException; + void generate(DdlWrite writer, AlterColumn alterColumn); /** * Write the drop column change. */ - void generate(DdlWrite writer, DropColumn dropColumn) throws IOException; + void generate(DdlWrite writer, DropColumn dropColumn); /** * Write the AddTableComment change. */ - void generate(DdlWrite writer, AddTableComment addTableComment) throws IOException; + void generate(DdlWrite writer, AddTableComment addTableComment); /** * Write the AddHistoryTable change. */ - void generate(DdlWrite writer, AddHistoryTable addHistoryTable) throws IOException; + void generate(DdlWrite writer, AddHistoryTable addHistoryTable); /** * Write the DropHistoryTable change. */ - void generate(DdlWrite writer, DropHistoryTable dropHistoryTable) throws IOException; + void generate(DdlWrite writer, DropHistoryTable dropHistoryTable); /** * Generate the create index change. */ - void generate(DdlWrite writer, CreateIndex createIndex) throws IOException; + void generate(DdlWrite writer, CreateIndex createIndex); /** * Write the drop index change. */ - void generate(DdlWrite writer, DropIndex dropIndex) throws IOException; + void generate(DdlWrite writer, DropIndex dropIndex); /** * Write add unique constraint. */ - void generate(DdlWrite writer, AddUniqueConstraint constraint) throws IOException; + void generate(DdlWrite writer, AddUniqueConstraint constraint); /** * Writes alter foreign key statements. */ - void generate(DdlWrite writer, AlterForeignKey alterForeignKey) throws IOException; + void generate(DdlWrite writer, AlterForeignKey alterForeignKey); /** * Generate any extra DDL such as stored procedures or TableValueParameters. */ - void generateProlog(DdlWrite write) throws IOException; + void generateProlog(DdlWrite writer); /** * Generate any extra DDL such as regeneration of history triggers. */ - void generateEpilog(DdlWrite write) throws IOException; + void generateEpilog(DdlWrite writer); } diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/AbstractHanaDdl.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/AbstractHanaDdl.java index f14c96762..69d0d2816 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/AbstractHanaDdl.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/AbstractHanaDdl.java @@ -1,14 +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.io.IOException; +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; @@ -18,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 "; @@ -39,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(); @@ -52,51 +55,21 @@ public abstract class AbstractHanaDdl extends PlatformDdl { : (alter.getDefaultValue() != null ? alter.getDefaultValue() : alter.getCurrentDefaultValue()); String defaultValueClause = (defaultValue == null || defaultValue.isEmpty()) ? "" : " default " + defaultValue; - try { - DdlBuffer buffer = new BaseDdlBuffer(null); - 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(defaultValueClause).append(notnullClause).append(alterColumnSuffix) - .endOfStatement(); + if (!isConvertible(currentType, type)) { + // add an intermediate conversion if possible + if (isNumberType(currentType)) { + // numbers can always be converted to decimal + alterTable(writer, tableName).append(alterColumn, columnName).append("decimal").append(notnullClause); - } else if (isStringType(currentType)) { - // strings can always be converted to nclob - buffer.append("alter table ").append(tableName).append(" ").append(alterColumn).append(" ").append(columnName) - .append(" nclob ").append(defaultValueClause).append(notnullClause).append(alterColumnSuffix) - .endOfStatement(); - } + } 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 + 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(); - } catch (IOException e) { - throw new RuntimeException(e); } - } - @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 @@ -120,20 +93,17 @@ public abstract class AbstractHanaDdl extends PlatformDdl { @Override public String alterTableDropUniqueConstraint(String tableName, String uniqueConstraintName) { - DdlBuffer buffer = new BaseDdlBuffer(null); - try { - buffer.append("delimiter $$").newLine(); - buffer.append("do").newLine(); - buffer.append("begin").newLine(); - buffer.append("declare exit handler for sql_error_code 397 begin end").endOfStatement(); - buffer.append("exec 'alter table ").append(tableName).append(" ").append(dropUniqueConstraint).append(" ") - .append(maxConstraintName(uniqueConstraintName)).append("'").endOfStatement(); - buffer.append("end").endOfStatement(); - buffer.append("$$"); - return buffer.getBuffer(); - } catch (IOException e) { - throw new RuntimeException(e); - } + DdlBuffer buffer = new BaseDdlBuffer(); + + buffer.append("delimiter $$").newLine(); + buffer.append("do").newLine(); + buffer.append("begin").newLine(); + buffer.append("declare exit handler for sql_error_code 397 begin end").endOfStatement(); + buffer.append("exec 'alter table ").append(tableName).append(" ").append(dropUniqueConstraint).append(" ") + .append(maxConstraintName(uniqueConstraintName)).append("'").endOfStatement(); + buffer.append("end").endOfStatement(); + buffer.append("$$"); + return buffer.getBuffer(); } @Override @@ -146,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) throws IOException { - 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("')"); } /** @@ -222,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 postProcessCommands(List cmds) { + List newCmds = new ArrayList<>(); + Map> batches = new LinkedHashMap<>(); + Set 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 newCmds, Map> batches) { + for (Entry> entry : batches.entrySet()) { + AlterCmd raw = newRawCommand("alter table ").append(tableName()).append(" ") + .append(entry.getKey()).append(" ("); + List 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(); + } + } + } diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/BaseAlterTableWrite.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/BaseAlterTableWrite.java new file mode 100644 index 000000000..bd777d5a5 --- /dev/null +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/BaseAlterTableWrite.java @@ -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 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 postProcessCommands(List cmds) { + return cmds; + } + + /** + * Writes the DDL to target. + */ + @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; + } +} diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/BaseDdlBuffer.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/BaseDdlBuffer.java index e54e91ba0..674c07063 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/BaseDdlBuffer.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/BaseDdlBuffer.java @@ -1,45 +1,34 @@ package io.ebeaninternal.dbmigration.ddlgeneration.platform; import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer; -import io.ebeaninternal.dbmigration.model.MConfiguration; -import java.io.IOException; -import java.io.StringWriter; /** * Base implementation of DdlBuffer using an underlying writer. */ public class BaseDdlBuffer implements DdlBuffer { - protected final StringWriter writer; + protected final StringBuilder writer; - protected final MConfiguration configuration; - - public BaseDdlBuffer(MConfiguration configuration) { - this.configuration = configuration; - this.writer = new StringWriter(); - } - - @Override - public MConfiguration getConfiguration() { - return configuration; + public BaseDdlBuffer() { + this.writer = new StringBuilder(); } @Override public boolean isEmpty() { - return writer.getBuffer().length() == 0; + return writer.length() == 0; } @Override - public DdlBuffer appendWithSpace(String foreignKeyRestrict) throws IOException { - if (foreignKeyRestrict != null && !foreignKeyRestrict.isEmpty()) { - writer.append(" ").append(foreignKeyRestrict); + public DdlBuffer appendWithSpace(String content) { + if (content != null && !content.isEmpty()) { + writer.append(" ").append(content); } return this; } @Override - public DdlBuffer appendStatement(String content) throws IOException { + public DdlBuffer appendStatement(String content) { if (content != null && !content.isEmpty()) { writer.append(content); endOfStatement(); @@ -48,19 +37,19 @@ public class BaseDdlBuffer implements DdlBuffer { } @Override - public DdlBuffer append(String content) throws IOException { + public DdlBuffer append(String content) { writer.append(content); return this; } @Override - public DdlBuffer append(String content, int space) throws IOException { + public DdlBuffer append(String content, int space) { writer.append(content); appendSpace(space, content); return this; } - protected void appendSpace(int max, String content) throws IOException { + protected void appendSpace(int max, String content) { int space = max - content.length(); if (space > 0) { for (int i = 0; i < space; i++) { @@ -71,7 +60,7 @@ public class BaseDdlBuffer implements DdlBuffer { } @Override - public DdlBuffer endOfStatement() throws IOException { + public DdlBuffer endOfStatement() { writer.append(";\n"); return this; } @@ -81,7 +70,7 @@ public class BaseDdlBuffer implements DdlBuffer { * This should be just whitespace or a sql comment. */ @Override - public DdlBuffer end() throws IOException { + public DdlBuffer end() { if (!isEmpty()) { writer.append("\n"); } @@ -89,7 +78,7 @@ public class BaseDdlBuffer implements DdlBuffer { } @Override - public DdlBuffer newLine() throws IOException { + public DdlBuffer newLine() { writer.append("\n"); return this; } diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/BaseTableDdl.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/BaseTableDdl.java index c4715fde3..da8c91d9f 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/BaseTableDdl.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/BaseTableDdl.java @@ -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; @@ -33,14 +32,9 @@ import io.ebeaninternal.dbmigration.model.MTable; import io.ebeaninternal.dbmigration.model.MTableIdentity; import io.ebeaninternal.server.deploy.IdentityMode; -import java.io.IOException; import java.util.ArrayList; import java.util.Collections; -import java.util.HashSet; -import java.util.LinkedHashMap; import java.util.List; -import java.util.Map; -import java.util.Set; import static io.ebean.util.StringHelper.replace; import static io.ebeaninternal.api.PlatformMatch.matchPlatform; @@ -51,12 +45,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; @@ -65,12 +53,6 @@ public class BaseTableDdl implements TableDdl { protected final String historyTableSuffix; - /** - * Used to check that indexes on foreign keys should be skipped as a unique index on the columns - * already exists. - */ - protected final IndexSet indexSet = new IndexSet(); - /** * Used when unique constraints specifically for OneToOne can't be created normally (MsSqlServer). */ @@ -78,26 +60,9 @@ public class BaseTableDdl implements TableDdl { protected final List externalCompoundUnique = new ArrayList<>(); - // counters used when constraint names are truncated due to maximum length - // and these counters are used to keep the constraint name unique - protected int countCheck; - protected int countUnique; - protected int countForeignKey; - protected int countIndex; - - /** - * 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 regenerateHistoryTriggers = new LinkedHashMap<>(); - - private final Set needsReorg = new HashSet<>(); - - private int reorgCount; - 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. @@ -148,32 +113,29 @@ public class BaseTableDdl implements TableDdl { after = getScriptsForPlatform(alter.getAfter()); } - void writeBefore(DdlBuffer buffer) throws IOException { + void write(DdlWrite writer) { if (!before.isEmpty()) { - buffer.end(); - flushReorgTables(buffer); + writer.apply().end(); if (withHistory) { - buffer.append("-- NOTE: table has @History - special migration may be necessary").newLine(); + writer.apply().append("-- NOTE: table has @History - special migration may be necessary").newLine(); } for (String ddlScript : before) { - buffer.appendStatement(translate(ddlScript, tableName, columnName, defaultValue)); + writer.apply().appendStatement(translate(ddlScript, tableName, columnName, defaultValue)); } } - - } - - void writeAfter(DdlBuffer buffer) throws IOException { + // 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()) { - if (withHistory) { - buffer.append("-- NOTE: table has @History - special migration may be necessary").newLine(); - } - flushReorgTables(buffer); - + writer.applyPostAlter().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.applyPostAlter().appendStatement(translate(ddlScript, tableName, columnName, defaultValue)); } - buffer.end(); + + writer.applyPostAlter().end(); } } @@ -221,9 +183,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(); } } @@ -231,13 +193,8 @@ public class BaseTableDdl implements TableDdl { * Reset counters and index set for each table processed. */ protected void reset() { - indexSet.clear(); externalUnique.clear(); externalCompoundUnique.clear(); - countCheck = 0; - countUnique = 0; - countForeignKey = 0; - countIndex = 0; } /** @@ -245,7 +202,7 @@ public class BaseTableDdl implements TableDdl { * and add them to the appropriate 'apply' and 'rollback' buffers. */ @Override - public void generate(DdlWrite writer, CreateTable createTable) throws IOException { + public void generate(DdlWrite writer, CreateTable createTable) { reset(); String tableName = lowerTableName(createTable.getName()); @@ -277,7 +234,7 @@ public class BaseTableDdl implements TableDdl { } } if (platformDdl.isInlineForeignKeys()) { - writeInlineForeignKeys(writer, createTable); + writeInlineForeignKeys(apply, createTable); } apply.newLine().append(")"); if (createTable.getTablespace() != null) { @@ -322,7 +279,7 @@ public class BaseTableDdl implements TableDdl { /** * Add table and column comments (separate from the create table statement). */ - private void addComments(DdlBuffer apply, CreateTable createTable) throws IOException { + private void addComments(DdlBuffer apply, CreateTable createTable) { if (!platformDdl.isInlineComments()) { String tableComment = createTable.getComment(); if (hasValue(tableComment)) { @@ -339,7 +296,7 @@ public class BaseTableDdl implements TableDdl { /** * Add the table storage engine clause. */ - private void addTableStorageEngine(DdlBuffer apply, CreateTable createTable) throws IOException { + private void addTableStorageEngine(DdlBuffer apply, CreateTable createTable) { if (platformDdl.isIncludeStorageEngine()) { platformDdl.tableStorageEngine(apply, createTable.getStorageEngine()); } @@ -348,7 +305,7 @@ public class BaseTableDdl implements TableDdl { /** * Add the table comment inline with the create table statement. */ - private void addTableCommentInline(DdlBuffer apply, CreateTable createTable) throws IOException { + private void addTableCommentInline(DdlBuffer apply, CreateTable createTable) { if (platformDdl.isInlineComments()) { String tableComment = createTable.getComment(); if (!StringHelper.isNull(tableComment)) { @@ -357,7 +314,7 @@ public class BaseTableDdl implements TableDdl { } } - private void writeTableColumns(DdlBuffer apply, List columns, DdlIdentity identity) throws IOException { + private void writeTableColumns(DdlBuffer apply, List columns, DdlIdentity identity) { platformDdl.writeTableColumns(apply, columns, identity); } @@ -365,7 +322,7 @@ public class BaseTableDdl implements TableDdl { * Specific handling of OneToOne unique constraints for MsSqlServer. * For all other DB platforms these unique constraints are done inline as per normal. */ - protected void writeUniqueOneToOneConstraints(DdlWrite write, CreateTable createTable) throws IOException { + protected void writeUniqueOneToOneConstraints(DdlWrite writer, CreateTable createTable) { String tableName = createTable.getName(); for (Column col : externalUnique) { String uqName = col.getUniqueOneToOne(); @@ -373,8 +330,9 @@ public class BaseTableDdl implements TableDdl { uqName = col.getUnique(); } String[] columnNames = {col.getName()}; - write.apply().appendStatement(platformDdl.alterTableAddUniqueConstraint(tableName, uqName, columnNames, Boolean.TRUE.equals(col.isNotnull()) ? null : columnNames)); - write.dropAllForeignKeys().appendStatement(platformDdl.dropIndex(uqName, tableName)); + 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)); } for (UniqueConstraint constraint : externalCompoundUnique) { @@ -382,12 +340,12 @@ public class BaseTableDdl implements TableDdl { String[] columnNames = split(constraint.getColumnNames()); String[] nullableColumns = split(constraint.getNullableColumns()); - write.apply().appendStatement(platformDdl.alterTableAddUniqueConstraint(tableName, uqName, columnNames, nullableColumns)); - write.dropAllForeignKeys().appendStatement(platformDdl.dropIndex(uqName, tableName)); + writer.applyPostAlter().appendStatement(platformDdl.alterTableAddUniqueConstraint(tableName, uqName, columnNames, nullableColumns)); + writer.dropAllForeignKeys().appendStatement(platformDdl.dropIndex(uqName, tableName)); } } - protected void writeSequence(DdlWrite writer, DdlIdentity identity) throws IOException { + protected void writeSequence(DdlWrite writer, DdlIdentity identity) { String seqName = identity.getSequenceName(); String createSeq = platformDdl.createSequence(seqName, identity); if (hasValue(createSeq)) { @@ -396,75 +354,75 @@ public class BaseTableDdl implements TableDdl { } } - protected void createWithHistory(DdlWrite writer, String name) throws IOException { + protected void createWithHistory(DdlWrite writer, String name) { MTable table = writer.getTable(name); platformDdl.createWithHistory(writer, table); } - protected void writeInlineForeignKeys(DdlWrite write, CreateTable createTable) throws IOException { + protected void writeInlineForeignKeys(DdlBuffer buffer, CreateTable createTable) { for (Column column : createTable.getColumn()) { String references = column.getReferences(); if (hasValue(references)) { - writeInlineForeignKey(write, column); + writeInlineForeignKey(buffer, column); } } - writeInlineCompoundForeignKeys(write, createTable); + writeInlineCompoundForeignKeys(buffer, createTable); } - protected void writeInlineForeignKey(DdlWrite write, Column column) throws IOException { + protected void writeInlineForeignKey(DdlBuffer buffer, Column column) { String fkConstraint = platformDdl.tableInlineForeignKey(new WriteForeignKey(null, column)); - write.apply().append(",").newLine().append(" ").append(fkConstraint); + buffer.append(",").newLine().append(" ").append(fkConstraint); } - protected void writeInlineCompoundForeignKeys(DdlWrite write, CreateTable createTable) throws IOException { + protected void writeInlineCompoundForeignKeys(DdlBuffer buffer, CreateTable createTable) { for (ForeignKey key : createTable.getForeignKey()) { String fkConstraint = platformDdl.tableInlineForeignKey(new WriteForeignKey(null, key)); - write.apply().append(",").newLine().append(" ").append(fkConstraint); + buffer.append(",").newLine().append(" ").append(fkConstraint); } } - protected void writeAddForeignKeys(DdlWrite write, CreateTable createTable) throws IOException { + protected void writeAddForeignKeys(DdlWrite writer, CreateTable createTable) { for (Column column : createTable.getColumn()) { String references = column.getReferences(); if (hasValue(references)) { - writeForeignKey(write, createTable.getName(), column); + writeForeignKey(writer, createTable.getName(), column); } } - writeAddCompoundForeignKeys(write, createTable); + writeAddCompoundForeignKeys(writer, createTable); } - protected void writeAddCompoundForeignKeys(DdlWrite write, CreateTable createTable) throws IOException { + protected void writeAddCompoundForeignKeys(DdlWrite writer, CreateTable createTable) { for (ForeignKey key : createTable.getForeignKey()) { - writeForeignKey(write, new WriteForeignKey(createTable.getName(), key)); + writeForeignKey(writer, new WriteForeignKey(createTable.getName(), key)); } } - protected void writeForeignKey(DdlWrite write, String tableName, Column column) throws IOException { - writeForeignKey(write, new WriteForeignKey(tableName, column)); + protected void writeForeignKey(DdlWrite writer, String tableName, Column column) { + writeForeignKey(writer, new WriteForeignKey(tableName, column)); } - protected void writeForeignKey(DdlWrite write, WriteForeignKey request) throws IOException { - DdlBuffer fkeyBuffer = write.applyForeignKeys(); + protected void writeForeignKey(DdlWrite writer, WriteForeignKey request) { + DdlBuffer fkeyBuffer = writer.applyForeignKeys(); String tableName = lowerTableName(request.table()); if (request.indexName() != null) { // no matching unique constraint so add the index fkeyBuffer.appendStatement(platformDdl.createIndex(new WriteCreateIndex(request.indexName(), tableName, request.cols(), false))); } - alterTableAddForeignKey(write.getOptions(), fkeyBuffer, request); + alterTableAddForeignKey(writer.getOptions(), fkeyBuffer, request); fkeyBuffer.end(); - write.dropAllForeignKeys().appendStatement(platformDdl.alterTableDropForeignKey(tableName, request.fkName())); + writer.dropAllForeignKeys().appendStatement(platformDdl.alterTableDropForeignKey(tableName, request.fkName())); if (hasValue(request.indexName())) { - write.dropAllForeignKeys().appendStatement(platformDdl.dropIndex(request.indexName(), tableName)); + writer.dropAllForeignKeys().appendStatement(platformDdl.dropIndex(request.indexName(), tableName)); } - write.dropAllForeignKeys().end(); + writer.dropAllForeignKeys().end(); } - protected void alterTableAddForeignKey(DdlOptions options, DdlBuffer buffer, WriteForeignKey request) throws IOException { + protected void alterTableAddForeignKey(DdlOptions options, DdlBuffer buffer, WriteForeignKey request) { buffer.appendStatement(platformDdl.alterTableAddForeignKey(options, request)); } - protected void appendColumns(String[] columns, DdlBuffer buffer) throws IOException { + protected void appendColumns(String[] columns, DdlBuffer buffer) { buffer.append(" ("); for (int i = 0; i < columns.length; i++) { if (i > 0) { @@ -478,18 +436,18 @@ public class BaseTableDdl implements TableDdl { /** * Add 'drop table' statement to the buffer. */ - protected void dropTable(DdlBuffer buffer, String tableName) throws IOException { + protected void dropTable(DdlBuffer buffer, String tableName) { buffer.appendStatement(platformDdl.dropTable(tableName)); } /** * Add 'drop sequence' statement to the buffer. */ - protected void dropSequence(DdlBuffer buffer, String sequenceName) throws IOException { + protected void dropSequence(DdlBuffer buffer, String sequenceName) { buffer.appendStatement(platformDdl.dropSequence(sequenceName)); } - protected void writeCompoundUniqueConstraints(DdlBuffer apply, CreateTable createTable) throws IOException { + protected void writeCompoundUniqueConstraints(DdlBuffer apply, CreateTable createTable) { boolean inlineUniqueWhenNull = platformDdl.isInlineUniqueWhenNullable(); for (UniqueConstraint uniqueConstraint : createTable.getUniqueConstraint()) { if (platformInclude(uniqueConstraint.getPlatforms())) { @@ -512,7 +470,7 @@ public class BaseTableDdl implements TableDdl { /** * Write the unique constraints inline with the create table statement. */ - protected void writeUniqueConstraints(DdlBuffer apply, CreateTable createTable) throws IOException { + protected void writeUniqueConstraints(DdlBuffer apply, CreateTable createTable) { boolean inlineUniqueWhenNullable = platformDdl.isInlineUniqueWhenNullable(); List columns = new WriteUniqueConstraint(createTable.getColumn()).uniqueKeys(); for (Column column : columns) { @@ -529,7 +487,7 @@ public class BaseTableDdl implements TableDdl { /** * Write the unique constraint inline with the create table statement. */ - protected void inlineUniqueConstraintSingle(DdlBuffer buffer, Column column) throws IOException { + protected void inlineUniqueConstraintSingle(DdlBuffer buffer, Column column) { String uqName = column.getUnique(); if (uqName == null) { uqName = column.getUniqueOneToOne(); @@ -544,7 +502,7 @@ public class BaseTableDdl implements TableDdl { /** * Write the primary key constraint inline with the create table statement. */ - protected void writePrimaryKeyConstraint(DdlBuffer buffer, String pkName, String[] pkColumns) throws IOException { + protected void writePrimaryKeyConstraint(DdlBuffer buffer, String pkName, String[] pkColumns) { buffer.append(",").newLine(); buffer.append(" constraint ").append(pkName).append(" primary key"); appendColumns(pkColumns, buffer); @@ -589,43 +547,43 @@ public class BaseTableDdl implements TableDdl { } @Override - public void generate(DdlWrite writer, CreateIndex index) throws IOException { + public void generate(DdlWrite writer, CreateIndex index) { if (platformInclude(index.getPlatforms())) { - flushReorgTables(writer.apply()); - 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()))); } } @Override - public void generate(DdlWrite writer, DropIndex dropIndex) throws IOException { + public void generate(DdlWrite writer, DropIndex dropIndex) { if (platformInclude(dropIndex.getPlatforms())) { - flushReorgTables(writer.apply()); - 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()))); } } @Override - public void generate(DdlWrite writer, AddUniqueConstraint constraint) throws IOException { + 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 { - flushReorgTables(writer.apply()); 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)); } } } @Override - public void generate(DdlWrite writer, AlterForeignKey alterForeignKey) throws IOException { + 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))); } } @@ -633,7 +591,7 @@ public class BaseTableDdl implements TableDdl { * Add add history table DDL. */ @Override - public void generate(DdlWrite writer, AddHistoryTable addHistoryTable) throws IOException { + public void generate(DdlWrite writer, AddHistoryTable addHistoryTable) { platformDdl.addHistoryTable(writer, addHistoryTable); } @@ -641,37 +599,27 @@ public class BaseTableDdl implements TableDdl { * Add drop history table DDL. */ @Override - public void generate(DdlWrite writer, DropHistoryTable dropHistoryTable) throws IOException { + public void generate(DdlWrite writer, DropHistoryTable dropHistoryTable) { platformDdl.dropHistoryTable(writer, dropHistoryTable); } @Override - public void generateProlog(DdlWrite write) throws IOException { - platformDdl.generateProlog(write); + public void generateProlog(DdlWrite writer) { + platformDdl.generateProlog(writer); } /** * Called at the end to generate additional ddl such as regenerate history triggers. */ @Override - public void generateEpilog(DdlWrite write) throws IOException { - if (!regenerateHistoryTriggers.isEmpty()) { - platformDdl.lockTables(write.applyHistoryTrigger(), regenerateHistoryTriggers.keySet()); - - for (HistoryTableUpdate update : this.regenerateHistoryTriggers.values()) { - platformDdl.regenerateHistoryTriggers(write, update); - } - - platformDdl.unlockTables(write.applyHistoryTrigger(), regenerateHistoryTriggers.keySet()); - } - platformDdl.generateEpilog(write); - flushReorgTables(write.apply()); + public void generateEpilog(DdlWrite writer) { + platformDdl.generateEpilog(writer); } @Override - public void generate(DdlWrite writer, AddTableComment addTableComment) throws IOException { + 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()); } } @@ -679,18 +627,20 @@ public class BaseTableDdl implements TableDdl { * Add add column DDL. */ @Override - public void generate(DdlWrite writer, AddColumn addColumn) throws IOException { + public void generate(DdlWrite writer, AddColumn addColumn) { String tableName = addColumn.getTableName(); List 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) { @@ -698,27 +648,26 @@ public class BaseTableDdl implements TableDdl { writeForeignKey(writer, tableName, column); } } - writer.apply().end(); } /** * Add drop table DDL. */ @Override - public void generate(DdlWrite writer, DropTable dropTable) throws IOException { - dropTable(writer.apply(), dropTable.getName()); + public void generate(DdlWrite writer, DropTable dropTable) { + 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); } } @Override - public void generate(DdlWrite writer, AlterTable alterTable) throws IOException { + public void generate(DdlWrite writer, AlterTable alterTable) { if (hasValue(alterTable.getTablespace()) || hasValue(alterTable.getIndexTablespace()) || hasValue(alterTable.getLobTablespace())) { writer.apply().appendStatement(platformDdl.alterTableTablespace(alterTable.getName(), DdlHelp.toTablespace(alterTable.getTablespace()), @@ -727,8 +676,7 @@ public class BaseTableDdl implements TableDdl { } } - protected void writeTablespaceChange(DdlBuffer buffer, String tablename, String tableSpace, String indexSpace, - String lobSpace) throws IOException { + protected void writeTablespaceChange(DdlBuffer buffer, String tablename, String tableSpace, String indexSpace, String lobSpace) { buffer.appendStatement("-- TableSpace changed: Table: " + tablename + ", tableSpace " + tableSpace + ", indexSpace " + indexSpace + ", lobSpace " + lobSpace); if (strictMode) { @@ -741,30 +689,32 @@ public class BaseTableDdl implements TableDdl { * Add drop column DDL. */ @Override - public void generate(DdlWrite writer, DropColumn dropColumn) throws IOException { + 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(); } /** * Add all the appropriate changes based on the column changes. */ @Override - public void generate(DdlWrite writer, AlterColumn alterColumn) throws IOException { + 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())) { @@ -796,31 +746,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) throws IOException { - platformDdl.addColumnComment(writer.apply(), alterColumn.getTableName(), alterColumn.getColumnName(), alterColumn.getComment()); + private void alterColumnComment(DdlWrite writer, AlterColumn alterColumn) { + platformDdl.addColumnComment(writer.applyPostAlter(), alterColumn.getTableName(), alterColumn.getColumnName(), alterColumn.getComment()); } /** @@ -831,124 +771,106 @@ 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) throws IOException { - 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) throws IOException { - writer.apply().appendStatement(platformDdl.alterColumnDefaultValue(alter.getTableName(), alter.getColumnName(), alter.getDefaultValue())); + protected void dropCheckConstraint(DdlWrite writer, AlterColumn alter, String constraintName) { + writer.applyDropDependencies() + .appendStatement(platformDdl.alterTableDropConstraint(alter.getTableName(), constraintName)); } - protected void dropCheckConstraint(DdlWrite writer, AlterColumn alter, String constraintName) throws IOException { - writer.apply().appendStatement(platformDdl.alterTableDropConstraint(alter.getTableName(), constraintName)); + protected void addCheckConstraint(DdlWrite writer, AlterColumn alter) { + writer.applyPostAlter() + .appendStatement(platformDdl.alterTableAddCheckConstraint(alter.getTableName(), alter.getCheckConstraintName(), alter.getCheckConstraint())); } - protected void addCheckConstraint(DdlWrite writer, AlterColumn alter) throws IOException { - writer.apply().appendStatement(platformDdl.alterTableAddCheckConstraint(alter.getTableName(), alter.getCheckConstraintName(), alter.getCheckConstraint())); + protected void alterColumnAddForeignKey(DdlWrite writer, AlterColumn alterColumn) { + alterTableAddForeignKey(writer.getOptions(), writer.applyForeignKeys(), new WriteForeignKey(alterColumn)); } - protected void alterColumnNotnull(DdlWrite writer, AlterColumn alter) throws IOException { - writer.apply().appendStatement(platformDdl.alterColumnNotnull(alter.getTableName(), alter.getColumnName(), alter.isNotnull())); - needsReorg.add(alter.getTableName()); + protected void alterColumnDropForeignKey(DdlWrite writer, AlterColumn alter) { + writer.applyDropDependencies() + .appendStatement(platformDdl.alterTableDropForeignKey(alter.getTableName(), alter.getDropForeignKey())); } - protected void alterColumnType(DdlWrite writer, AlterColumn alter) throws IOException { - String ddl = platformDdl.alterColumnType(alter.getTableName(), alter.getColumnName(), alter.getType()); - if (hasValue(ddl)) { - needsReorg.add(alter.getTableName()); - 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 alterColumnDropUniqueConstraint(DdlWrite writer, AlterColumn alter) { + writer.applyDropDependencies() + .appendStatement(platformDdl.alterTableDropUniqueConstraint(alter.getTableName(), alter.getDropUnique())); } - protected void alterColumnAddForeignKey(DdlWrite writer, AlterColumn alterColumn) throws IOException { - alterTableAddForeignKey(writer.getOptions(), writer.apply(), new WriteForeignKey(alterColumn)); - } - - protected void alterColumnDropForeignKey(DdlWrite writer, AlterColumn alter) throws IOException { - writer.apply().appendStatement(platformDdl.alterTableDropForeignKey(alter.getTableName(), alter.getDropForeignKey())); - } - - protected void alterColumnDropUniqueConstraint(DdlWrite writer, AlterColumn alter) throws IOException { - writer.apply().appendStatement(platformDdl.alterTableDropUniqueConstraint(alter.getTableName(), alter.getDropUnique())); - } - - protected void alterColumnAddUniqueOneToOneConstraint(DdlWrite writer, AlterColumn alter) throws IOException { + protected void alterColumnAddUniqueOneToOneConstraint(DdlWrite writer, AlterColumn alter) { addUniqueConstraint(writer, alter, alter.getUniqueOneToOne()); } - protected void alterColumnAddUniqueConstraint(DdlWrite writer, AlterColumn alter) throws IOException { + protected void alterColumnAddUniqueConstraint(DdlWrite writer, AlterColumn alter) { addUniqueConstraint(writer, alter, alter.getUnique()); } - protected void addUniqueConstraint(DdlWrite writer, AlterColumn alter, String uqName) throws IOException { + 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) throws IOException { - platformDdl.alterTableDropColumn(buffer, tableName, columnName); - needsReorg.add(tableName); + 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) throws IOException { + 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 void flushReorgTables(DdlBuffer buffer) throws IOException { - for (String table : needsReorg) { - buffer.appendStatement(platformDdl.reorgTable(table, ++reorgCount)); - } - needsReorg.clear(); } protected boolean isFalse(Boolean value) { diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/ClickHouseDbArray.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/ClickHouseDbArray.java index 0bb2f7895..469e58420 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/ClickHouseDbArray.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/ClickHouseDbArray.java @@ -15,6 +15,7 @@ class ClickHouseDbArray { mapping.put("integer[]", "Array(UInt32)"); mapping.put("bigint[]", "Array(UInt64)"); mapping.put("float[]", "Array(Float32)"); + mapping.put("decimal[]", "Array(Decimal)"); } /** diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/ClickHouseDdl.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/ClickHouseDdl.java index 97f3bf8c7..940cdcf24 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/ClickHouseDdl.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/ClickHouseDdl.java @@ -6,8 +6,6 @@ import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer; import io.ebeaninternal.dbmigration.ddlgeneration.DdlHandler; import io.ebeaninternal.dbmigration.ddlgeneration.DdlOptions; -import java.io.IOException; - public class ClickHouseDdl extends PlatformDdl { private static final String LOG_TABLE = "ENGINE = Log()"; @@ -16,6 +14,7 @@ public class ClickHouseDdl extends PlatformDdl { super(platform); this.includeStorageEngine = true; this.identitySuffix = ""; + this.columnNotNull = null; } @Override @@ -32,7 +31,7 @@ public class ClickHouseDdl extends PlatformDdl { * Add an table storage engine to the create table statement. */ @Override - public void tableStorageEngine(DdlBuffer apply, String storageEngine) throws IOException { + public void tableStorageEngine(DdlBuffer apply, String storageEngine) { if (storageEngine == null) { // default to Log() table but really should all be explicit (need arguments for MergeTree etc) storageEngine = LOG_TABLE; @@ -70,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 diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/CockroachDdl.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/CockroachDdl.java index 7a14001fa..700000583 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/CockroachDdl.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/CockroachDdl.java @@ -36,6 +36,12 @@ public class CockroachDdl extends PlatformDdl { // do nothing } + @Override + public String alterTableDropUniqueConstraint(String tableName, String uniqueConstraintName) { + // workaround for: https://github.com/cockroachdb/cockroach/issues/42840 + return "drop index " + maxConstraintName(uniqueConstraintName) + " cascade"; + } + @Override public boolean isInlineComments() { return false; diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/DB2Ddl.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/DB2Ddl.java index aa1a95494..67c3eb133 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/DB2Ddl.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/DB2Ddl.java @@ -1,10 +1,14 @@ package io.ebeaninternal.dbmigration.ddlgeneration.platform; -import java.io.IOException; - -import io.ebean.annotation.ConstraintMode; import io.ebean.config.dbplatform.DatabasePlatform; import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer; +import java.util.ArrayList; +import java.util.List; + +import io.ebean.util.StringHelper; +import io.ebeaninternal.dbmigration.ddlgeneration.DdlAlterTable; +import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite; +import io.ebeaninternal.dbmigration.migration.Column; /** * DB2 platform specific DDL. @@ -44,6 +48,7 @@ public class DB2Ddl extends PlatformDdl { this.columnSetNull = "drop not null"; this.columnSetType = "set data type "; this.inlineUniqueWhenNullable = false; + this.historyDdl = new Db2HistoryDdl(); } @Override @@ -71,8 +76,9 @@ public class DB2Ddl extends PlatformDdl { if (uqName == null) { throw new NullPointerException(); } - sb.append("create unique index ").append(maxConstraintName(uqName)); - sb.append(" on ").append(lowerTableName(tableName)).append('('); + sb = new StringBuilder("create unique index "); + sb.append(maxConstraintName(uqName)).append(" on ").append(tableName).append('('); + for (int i = 0; i < columns.length; i++) { if (i > 0) { sb.append(","); @@ -83,17 +89,82 @@ public class DB2Ddl extends PlatformDdl { return sb.toString(); } + + // @Override + // public void addTablespace(DdlBuffer apply, String tablespaceName, String indexTablespace, String lobTablespace) + // throws IOException { + // apply.append(" in ").append(tablespaceName).append(" index in ").append(indexTablespace).append(" long in ").append(lobTablespace); + // } + + @Override + 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); + return alterTableDropConstraint(tableName, uniqueConstraintName) + + "\n" + dropIndex(uniqueConstraintName, tableName); } @Override - protected void appendForeignKeyOnUpdate(StringBuilder buffer, ConstraintMode mode) { - // do nothing, no on update clause for db2 + 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) { @@ -101,61 +172,68 @@ public class DB2Ddl extends PlatformDdl { 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"); + .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 - public String alterTableDropForeignKey(String tableName, String fkName) { - return alterTableDropConstraint(tableName, fkName); - } - @Override - public String alterTableDropConstraint(String tableName, String constraintName) { - StringBuilder sb = new StringBuilder(300); - sb.append("delimiter $$\n"); - sb.append("begin\n"); - sb.append("if exists (select constname from syscat.tabconst where tabschema = current_schema and constname = '"); - sb.append(maxConstraintName(constraintName).toUpperCase()); - sb.append("' and tabname = '").append(lowerTableName(tableName).toUpperCase()).append("') then\n"); - - sb.append(" prepare stmt from 'alter table ").append(lowerTableName(tableName)); - sb.append(" drop constraint ").append(maxConstraintName(constraintName)).append("';\n"); - - sb.append(" execute stmt;\n"); - sb.append("end if;\n"); - sb.append("end$$"); - return sb.toString(); - } - - @Override - public String dropIndex(String indexName, String tableName, boolean concurrent) { - StringBuilder sb = new StringBuilder(300); - sb.append("delimiter $$\n"); - sb.append("begin\n"); - sb.append("if exists (select indname from syscat.indexes where indschema = current_schema and indname = '"); - sb.append(maxConstraintName(indexName).toUpperCase()).append("') then\n"); +@Override +protected DdlAlterTable alterTable(DdlWrite writer, String tableName) { + return writer.applyAlterTable(tableName, Db2AlterTableWrite::new); +}; + static class Db2AlterTableWrite extends BaseAlterTableWrite { - sb.append(" prepare stmt from 'drop index ").append(maxConstraintName(indexName)).append("';\n"); - sb.append(" execute stmt;\n"); - sb.append("end if;\n"); - sb.append("end$$"); - return sb.toString(); - } + public Db2AlterTableWrite(String tableName) { + super(tableName); + } - @Override - public String reorgTable(String table, int counter) { - return "call sysproc.admin_cmd('reorg table " + lowerTableName(table) + "') /* reorg #" + counter + " */"; + @Override + protected List postProcessCommands(List cmds) { + List 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: + *

    + *
  • DROP COLUMN + *
  • ALTER COLUMN SET NOT NULL + *
  • ALTER COLUMN DROP NOT NULL + *
  • ALTER COLUMN SET DATA TYPE, except in the following situations:
    + * Increasing the length of a VARCHAR or VARGRAPHIC column
    + * Decreasing the length of a VARCHAR or VARGRAPHIC column without truncating trailing blanks from existing data, when no indexes + * exist on the column + *
+ * + */ + 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; + } + } } - - @Override - public void addTablespace(DdlBuffer apply, String tablespaceName, String indexTablespace, String lobTablespace) - throws IOException { - apply.append(" in ").append(tablespaceName).append(" index in ").append(indexTablespace).append(" long in ").append(lobTablespace); - } - } diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/Db2HistoryDdl.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/Db2HistoryDdl.java new file mode 100644 index 000000000..f1409974d --- /dev/null +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/Db2HistoryDdl.java @@ -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 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(); + } + +} diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/DbTriggerBasedHistoryDdl.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/DbTriggerBasedHistoryDdl.java index 58bed4505..a7457b0ae 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/DbTriggerBasedHistoryDdl.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/DbTriggerBasedHistoryDdl.java @@ -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; @@ -9,8 +10,6 @@ import io.ebeaninternal.dbmigration.migration.DropHistoryTable; import io.ebeaninternal.dbmigration.model.MColumn; import io.ebeaninternal.dbmigration.model.MTable; -import java.io.IOException; -import java.util.Collection; import java.util.List; /** @@ -49,51 +48,20 @@ public abstract class DbTriggerBasedHistoryDdl implements PlatformHistoryDdl { } @Override - public void updateTriggers(DdlWrite writer, HistoryTableUpdate update) throws IOException { - - 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) throws IOException; - - /** - * Process the HistoryTableUpdate which can result in changes to the apply, rollback - * and drop scripts. - */ - protected void updateTriggers(DdlWrite writer, MTable table, HistoryTableUpdate update) throws IOException { - - writer.applyHistoryTrigger().append("-- changes: ").append(update.description()).newLine(); - - updateHistoryTriggers(createDbTriggerUpdate(writer, table)); - } - - protected DbTriggerUpdate createDbTriggerUpdate(DdlWrite writer, MTable table) { - - List 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) throws IOException { + public void dropHistoryTable(DdlWrite writer, DropHistoryTable dropHistoryTable) { String baseTable = dropHistoryTable.getBaseTable(); // drop in appropriate order dropTriggers(writer.applyDropDependencies(), baseTable); - dropHistoryTableEtc(writer.applyDropDependencies(), baseTable); + dropWithHistoryView(writer.applyDropDependencies(), baseTable); + dropHistoryTable(writer.applyDropDependencies(), baseTable); + + dropSysPeriodColumns(writer, baseTable); } @Override - public void addHistoryTable(DdlWrite writer, AddHistoryTable addHistoryTable) throws IOException { + public void addHistoryTable(DdlWrite writer, AddHistoryTable addHistoryTable) { String baseTable = addHistoryTable.getBaseTable(); MTable table = writer.getTable(baseTable); @@ -105,27 +73,55 @@ public abstract class DbTriggerBasedHistoryDdl implements PlatformHistoryDdl { } @Override - public void createWithHistory(DdlWrite writer, MTable table) throws IOException { + 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) throws IOException; + @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)); - protected abstract void dropTriggers(DdlBuffer buffer, String baseTable) throws IOException; - - protected void createStoredFunction(DdlWrite writer, MTable table) throws IOException { - // do nothing + 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 columnNames); + + protected abstract void dropTriggers(DdlBuffer buffer, String baseTable); + + protected String normalise(String tableName) { return constraintNaming.normaliseTable(tableName); } @@ -150,35 +146,32 @@ public abstract class DbTriggerBasedHistoryDdl implements PlatformHistoryDdl { return normalise(baseTableName) + "_history_del"; } - protected void addHistoryTable(DdlWrite writer, MTable table, String whenCreatedColumn) throws IOException { + protected void addHistoryTable(DdlWrite writer, MTable table, String whenCreatedColumn) { 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) throws IOException { - - 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(); } } - protected void createHistoryTable(DdlBuffer apply, MTable table) throws IOException { + protected void createHistoryTable(DdlBuffer apply, MTable table) { createHistoryTableAs(apply, table); createHistoryTableWithPeriod(apply); } - protected void createHistoryTableAs(DdlBuffer apply, MTable table) throws IOException { + protected void createHistoryTableAs(DdlBuffer apply, MTable table) { apply.append(platformDdl.getCreateTableCommandPrefix()).append(" ").append(table.getName()).append(historySuffix).append("(").newLine(); for (MColumn column : table.allColumns()) { if (!column.isDraftOnly()) { @@ -188,7 +181,7 @@ public abstract class DbTriggerBasedHistoryDdl implements PlatformHistoryDdl { } } - protected void createHistoryTableWithPeriod(DdlBuffer apply) throws IOException { + protected void createHistoryTableWithPeriod(DdlBuffer apply) { writeColumnDefinition(apply, sysPeriodStart, sysPeriodType); apply.append(",").newLine(); writeColumnDefinition(apply, sysPeriodEnd, sysPeriodType); @@ -198,7 +191,7 @@ public abstract class DbTriggerBasedHistoryDdl implements PlatformHistoryDdl { /** * Write the column definition to the create table statement. */ - protected void writeColumnDefinition(DdlBuffer buffer, String columnName, String type) throws IOException { + protected void writeColumnDefinition(DdlBuffer buffer, String columnName, String type) { String platformType = platformDdl.convert(type); buffer.append(" "); @@ -206,57 +199,43 @@ public abstract class DbTriggerBasedHistoryDdl implements PlatformHistoryDdl { buffer.append(platformType); } - protected void createWithHistoryView(DdlBuffer apply, String baseTableName) throws IOException { + protected void createWithHistoryView(DdlBuffer apply, String baseTableName) { apply .append("create view ").append(baseTableName).append(viewSuffix) .append(" as select * from ").append(baseTableName) .append(" union all select * from ").append(baseTableName).append(historySuffix) - .endOfStatement().end(); + .endOfStatement(); } - - /** - * 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) throws IOException { - - 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()); - } - - protected void appendSysPeriodColumns(DdlBuffer apply, String prefix) throws IOException { + protected void appendSysPeriodColumns(DdlBuffer apply, String prefix) { appendColumnName(apply, prefix, sysPeriodStart); appendColumnName(apply, prefix, sysPeriodEnd); } - protected void dropHistoryTableEtc(DdlBuffer buffer, String baseTableName) throws IOException { - - 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) throws IOException { - 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 columns) throws IOException { + 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 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."); buffer.append(");").newLine(); } - void appendColumnNames(DdlBuffer buffer, List columns, String columnPrefix) throws IOException { + void appendColumnNames(DdlBuffer buffer, List columns, String columnPrefix) { for (int i = 0; i < columns.size(); i++) { if (i > 0) { buffer.append(", "); @@ -269,7 +248,7 @@ public abstract class DbTriggerBasedHistoryDdl implements PlatformHistoryDdl { /** * Append a single column to the buffer if it is not null. */ - void appendColumnName(DdlBuffer buffer, String prefix, String columnName) throws IOException { + void appendColumnName(DdlBuffer buffer, String prefix, String columnName) { if (columnName != null) { buffer.append(prefix).append(columnName); } @@ -287,4 +266,8 @@ public abstract class DbTriggerBasedHistoryDdl implements PlatformHistoryDdl { return table.allHistoryColumns(true); } + @Override + public boolean alterHistoryTables() { + return true; + } } diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/DbTriggerUpdate.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/DbTriggerUpdate.java deleted file mode 100644 index fa90a233e..000000000 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/DbTriggerUpdate.java +++ /dev/null @@ -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 columns; - - public DbTriggerUpdate(String baseTableName, String historyTableName, DdlWrite writer, List 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 getColumns() { - return columns; - } - -} diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/H2HistoryDdl.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/H2HistoryDdl.java index 5bd58adeb..7e5d2aa87 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/H2HistoryDdl.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/H2HistoryDdl.java @@ -1,10 +1,8 @@ package io.ebeaninternal.dbmigration.ddlgeneration.platform; -import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer; -import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite; -import io.ebeaninternal.dbmigration.model.MTable; +import java.util.List; -import java.io.IOException; +import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer; /** * H2 history support using DB triggers to maintain a history table. @@ -20,26 +18,16 @@ public class H2HistoryDdl extends DbTriggerBasedHistoryDdl { } @Override - protected void dropTriggers(DdlBuffer buffer, String baseTable) throws IOException { + protected void dropTriggers(DdlBuffer buffer, String baseTable) { buffer.append("drop trigger ").append(updateTriggerName(baseTable)).endOfStatement(); } @Override - protected void createTriggers(DdlWrite writer, MTable table) throws IOException { - String baseTableName = table.getName(); - DdlBuffer apply = writer.applyHistoryTrigger(); - addCreateTrigger(apply, updateTriggerName(baseTableName), baseTableName); + protected void createTriggers(DdlBuffer buffer, String baseTable, List columnNames) { + addCreateTrigger(buffer, updateTriggerName(baseTable), baseTable); } - @Override - protected void updateHistoryTriggers(DbTriggerUpdate update) throws IOException { - recreateHistoryView(update); - DdlBuffer buffer = update.historyTriggerBuffer(); - dropTriggers(buffer, update.getBaseTable()); - addCreateTrigger(buffer, updateTriggerName(update.getBaseTable()), update.getBaseTable()); - } - - private void addCreateTrigger(DdlBuffer apply, String triggerName, String baseTable) throws IOException { + private void addCreateTrigger(DdlBuffer apply, String triggerName, String baseTable) { // Note that this does not take into account the historyTable name (excepts _history suffix) and // does not take into account excluded columns (all columns included in history) apply diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/HanaColumnStoreDdl.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/HanaColumnStoreDdl.java index 872fc3930..b1fbd08a8 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/HanaColumnStoreDdl.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/HanaColumnStoreDdl.java @@ -3,8 +3,6 @@ package io.ebeaninternal.dbmigration.ddlgeneration.platform; import io.ebean.config.dbplatform.DatabasePlatform; import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer; -import java.io.IOException; - public class HanaColumnStoreDdl extends AbstractHanaDdl { public HanaColumnStoreDdl(DatabasePlatform platform) { @@ -31,20 +29,16 @@ public class HanaColumnStoreDdl extends AbstractHanaDdl { @Override public String dropIndex(String indexName, String tableName, boolean concurrent) { - DdlBuffer buffer = new BaseDdlBuffer(null); - try { - buffer.append("delimiter $$").newLine(); - buffer.append("do").newLine(); - buffer.append("begin").newLine(); - buffer.append("declare exit handler for sql_error_code 261 begin end").endOfStatement(); - buffer.append("exec '").append(dropIndexIfExists).append(maxConstraintName(indexName)).append("'") - .endOfStatement(); - buffer.append("end").endOfStatement(); - buffer.append("$$"); - return buffer.getBuffer(); - } catch (IOException e) { - throw new RuntimeException(e); - } + DdlBuffer buffer = new BaseDdlBuffer(); + buffer.append("delimiter $$").newLine(); + buffer.append("do").newLine(); + buffer.append("begin").newLine(); + buffer.append("declare exit handler for sql_error_code 261 begin end").endOfStatement(); + buffer.append("exec '").append(dropIndexIfExists).append(maxConstraintName(indexName)).append("'") + .endOfStatement(); + buffer.append("end").endOfStatement(); + buffer.append("$$"); + return buffer.getBuffer(); } } diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/HanaDdlHandler.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/HanaDdlHandler.java deleted file mode 100644 index 47172dc30..000000000 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/HanaDdlHandler.java +++ /dev/null @@ -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)); - } -} diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/HanaHistoryDdl.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/HanaHistoryDdl.java index 3624fa1da..f0f4cf8ac 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/HanaHistoryDdl.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/HanaHistoryDdl.java @@ -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; @@ -8,11 +9,7 @@ import io.ebeaninternal.dbmigration.migration.DropHistoryTable; import io.ebeaninternal.dbmigration.model.MColumn; import io.ebeaninternal.dbmigration.model.MTable; -import java.io.IOException; import java.util.Collection; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.atomic.AtomicInteger; public class HanaHistoryDdl implements PlatformHistoryDdl { @@ -20,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 createdHistoryTables = new ConcurrentHashMap<>(); @Override public void configure(DatabaseConfig config, PlatformDdl platformDdl) { @@ -32,13 +27,10 @@ public class HanaHistoryDdl implements PlatformHistoryDdl { } @Override - public void createWithHistory(DdlWrite writer, MTable table) throws IOException { + 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(); @@ -65,20 +57,19 @@ 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); } @Override - public void dropHistoryTable(DdlWrite writer, DropHistoryTable dropHistoryTable) throws IOException { + public void dropHistoryTable(DdlWrite writer, DropHistoryTable dropHistoryTable) { dropHistoryTable(writer.applyDropDependencies(), dropHistoryTable.getBaseTable(), dropHistoryTable.getBaseTable() + historySuffix); } - protected void dropHistoryTable(DdlBuffer apply, String baseTable, String historyTable) throws IOException { + protected void dropHistoryTable(DdlBuffer apply, String baseTable, String historyTable) { // disable system versioning disableSystemVersioning(apply, baseTable); @@ -93,7 +84,7 @@ public class HanaHistoryDdl implements PlatformHistoryDdl { } @Override - public void addHistoryTable(DdlWrite writer, AddHistoryTable addHistoryTable) throws IOException { + 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)"); @@ -102,12 +93,23 @@ public class HanaHistoryDdl implements PlatformHistoryDdl { } @Override - public void updateTriggers(DdlWrite write, 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, - boolean isNotNull, String generated) throws IOException { + boolean isNotNull, String generated) { String platformType = platformDdl.convert(type); buffer.append(" ").append(platformDdl.lowerColumnName(columnName)); @@ -123,35 +125,16 @@ public class HanaHistoryDdl implements PlatformHistoryDdl { } } - public void disableSystemVersioning(DdlBuffer apply, String tableName) throws IOException { - disableSystemVersioning(apply, tableName, false); + public void disableSystemVersioning(DdlBuffer apply, String tableName) { + apply.append("alter table ").append(tableName).append(" drop system versioning").endOfStatement(); } - public void disableSystemVersioning(DdlBuffer apply, String tableName, boolean uniqueStatement) throws IOException { - 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) throws IOException { - 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); - } } diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/HanaTableDdl.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/HanaTableDdl.java deleted file mode 100644 index 341fe9517..000000000 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/HanaTableDdl.java +++ /dev/null @@ -1,131 +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.io.IOException; -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) throws IOException { - // done in alterColumnBaseAttributes - } - - @Override - public void generate(DdlWrite writer, AddColumn addColumn) throws IOException { - 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 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) throws IOException { - 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) throws IOException { - 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); - } - } - -} diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/HistoryTableUpdate.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/HistoryTableUpdate.java deleted file mode 100644 index a72350a1f..000000000 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/HistoryTableUpdate.java +++ /dev/null @@ -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 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; - } - -} diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/MariaDbHistoryDdl.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/MariaDbHistoryDdl.java index 3fbdfad61..31d3cdd91 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/MariaDbHistoryDdl.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/MariaDbHistoryDdl.java @@ -1,53 +1,59 @@ 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.DropHistoryTable; import io.ebeaninternal.dbmigration.model.MTable; -import java.io.IOException; - /** * History DDL for MariaDB. */ public class MariaDbHistoryDdl implements PlatformHistoryDdl { + private PlatformDdl platformDdl; + @Override public void configure(DatabaseConfig config, PlatformDdl platformDdl) { - // do nothing + this.platformDdl = platformDdl; } @Override - public void createWithHistory(DdlWrite writer, MTable table) throws IOException { + public void createWithHistory(DdlWrite writer, MTable table) { String baseTable = table.getName(); enableSystemVersioning(writer, baseTable); } - private void enableSystemVersioning(DdlWrite writer, String baseTable) throws IOException { - DdlBuffer apply = writer.applyHistoryView(); - apply.append("alter table ").append(baseTable).append(" add system versioning").endOfStatement(); + private void enableSystemVersioning(DdlWrite writer, String baseTable) { + platformDdl.alterTable(writer, baseTable).append("add system versioning", null); DdlBuffer drop = writer.dropAll(); drop.append("alter table ").append(baseTable).append(" drop system versioning").endOfStatement(); } @Override - public void dropHistoryTable(DdlWrite writer, DropHistoryTable dropHistoryTable) throws IOException { + 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 - public void addHistoryTable(DdlWrite writer, AddHistoryTable addHistoryTable) throws IOException { + public void addHistoryTable(DdlWrite writer, AddHistoryTable addHistoryTable) { String baseTable = addHistoryTable.getBaseTable(); enableSystemVersioning(writer, baseTable); } @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(); + } } } diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/MySqlDdl.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/MySqlDdl.java index c772a30aa..d8ec08128 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/MySqlDdl.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/MySqlDdl.java @@ -3,12 +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.io.IOException; -import java.util.Collection; - /** * MySql specific DDL. */ @@ -22,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; @@ -38,11 +36,11 @@ public class MySqlDdl extends PlatformDdl { } @Override - public void alterTableDropColumn(final DdlBuffer buffer, final String tableName, final String columnName) throws IOException { + 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); } } @@ -92,42 +90,35 @@ 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 - protected void writeColumnDefinition(DdlBuffer buffer, Column column, DdlIdentity identity) throws IOException { + protected void writeColumnDefinition(DdlBuffer buffer, Column column, DdlIdentity identity) { super.writeColumnDefinition(buffer, column, identity); String comment = column.getComment(); if (!StringHelper.isNull(comment)) { @@ -140,7 +131,7 @@ public class MySqlDdl extends PlatformDdl { } @Override - public void inlineTableComment(DdlBuffer apply, String tableComment) throws IOException { + public void inlineTableComment(DdlBuffer apply, String tableComment) { if (tableComment.length() > 1000) { tableComment = tableComment.substring(0, 1000); } @@ -151,7 +142,7 @@ public class MySqlDdl extends PlatformDdl { * Add table comment as a separate statement (from the create table statement). */ @Override - public void addTableComment(DdlBuffer apply, String tableName, String tableComment) throws IOException { + public void addTableComment(DdlBuffer apply, String tableName, String tableComment) { if (DdlHelp.isDropComment(tableComment)) { tableComment = ""; } @@ -163,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 tables) throws IOException { - 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 tables) throws IOException { - buffer.append("unlock tables").endOfStatement(); - } - } diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/MySqlHistoryDdl.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/MySqlHistoryDdl.java index 16d07136e..7a9e06730 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/MySqlHistoryDdl.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/MySqlHistoryDdl.java @@ -1,10 +1,8 @@ package io.ebeaninternal.dbmigration.ddlgeneration.platform; -import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer; -import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite; -import io.ebeaninternal.dbmigration.model.MTable; +import java.util.List; -import java.io.IOException; +import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer; /** * MySql history support using DB triggers to maintain a history table. @@ -15,54 +13,41 @@ public class MySqlHistoryDdl extends DbTriggerBasedHistoryDdl { } @Override - protected void dropTriggers(DdlBuffer buffer, String baseTable) throws IOException { + protected void dropTriggers(DdlBuffer buffer, String baseTable) { buffer.append("drop trigger ").append(updateTriggerName(baseTable)).endOfStatement(); buffer.append("drop trigger ").append(deleteTriggerName(baseTable)).endOfStatement(); } - @Override - protected void createTriggers(DdlWrite writer, MTable table) throws IOException { - - DbTriggerUpdate update = createDbTriggerUpdate(writer, table); - - addBeforeUpdate(updateTriggerName(update.getBaseTable()), update); - addBeforeDelete(deleteTriggerName(update.getBaseTable()), update); - } @Override - protected void updateHistoryTriggers(DbTriggerUpdate update) throws IOException { + protected void createTriggers(DdlBuffer buffer, String baseTable, List 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) throws IOException { + private void addBeforeUpdate(DdlBuffer apply, String triggerName, String tableName, List 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) throws IOException { + private void addBeforeDelete(DdlBuffer apply, String triggerName, String tableName, List 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(); } diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/NoHistorySupportDdl.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/NoHistorySupportDdl.java index fbaab9be8..b580f20cb 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/NoHistorySupportDdl.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/NoHistorySupportDdl.java @@ -6,8 +6,6 @@ import io.ebeaninternal.dbmigration.migration.AddHistoryTable; import io.ebeaninternal.dbmigration.migration.DropHistoryTable; import io.ebeaninternal.dbmigration.model.MTable; -import java.io.IOException; - /** * Default history implementation that does nothing. Needs to be replaced * with an appropriate implementation for the given database platform. @@ -20,7 +18,7 @@ public class NoHistorySupportDdl implements PlatformHistoryDdl { } @Override - public void createWithHistory(DdlWrite writer, MTable table) throws IOException { + public void createWithHistory(DdlWrite writer, MTable table) { // does nothing } @@ -30,12 +28,8 @@ public class NoHistorySupportDdl implements PlatformHistoryDdl { } @Override - public void addHistoryTable(DdlWrite writer, AddHistoryTable addHistoryTable) throws IOException { + public void addHistoryTable(DdlWrite writer, AddHistoryTable addHistoryTable) { // does nothing } - @Override - public void updateTriggers(DdlWrite write, HistoryTableUpdate update) { - // does nothing - } } diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/NuoDbHistoryDdl.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/NuoDbHistoryDdl.java index 89d81eb9b..b92b07640 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/NuoDbHistoryDdl.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/NuoDbHistoryDdl.java @@ -1,10 +1,9 @@ package io.ebeaninternal.dbmigration.ddlgeneration.platform; -import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer; -import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite; -import io.ebeaninternal.dbmigration.model.MTable; -import java.io.IOException; +import java.util.List; + +import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer; /** * NuoDB history support using DB triggers to maintain a history table. @@ -17,59 +16,43 @@ public class NuoDbHistoryDdl extends DbTriggerBasedHistoryDdl { } @Override - protected void dropTriggers(DdlBuffer buffer, String baseTable) throws IOException { + protected void dropTriggers(DdlBuffer buffer, String baseTable) { buffer.append("drop trigger ").append(updateTriggerName(baseTable)).endOfStatement(); buffer.append("drop trigger ").append(deleteTriggerName(baseTable)).endOfStatement(); } @Override - protected void createTriggers(DdlWrite writer, MTable table) throws IOException { + protected void createTriggers(DdlBuffer buffer, String baseTable, List 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) throws IOException { + private void addBeforeUpdate(DdlBuffer apply, String triggerName, String tableName, List 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) throws IOException { - - 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) throws IOException { + private void addBeforeDelete(DdlBuffer apply, String triggerName, String tableName, List 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) throws IOException { + 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(); } - private void addEndTrigger(DdlBuffer apply) throws IOException { + private void addEndTrigger(DdlBuffer apply) { apply.append("end_trigger") .endOfStatement() .append("$$").newLine() diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/OracleDdl.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/OracleDdl.java index 740870b09..f94b559d2 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/OracleDdl.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/OracleDdl.java @@ -20,6 +20,8 @@ public class OracleDdl extends PlatformDdl { this.columnSetNotnull = "not null"; this.columnSetNull = "null"; this.columnSetDefault = "default"; + this.columnDropDefault = "default null"; // dropping default is not supported. See: + // https://stackoverflow.com/questions/8481532/how-do-i-remove-the-default-value-from-a-column-in-oracle this.identitySuffix = " generated by default as identity"; } @@ -33,6 +35,21 @@ public class OracleDdl extends PlatformDdl { } } + @Override + public String dropSequence(String sequenceName) { + return ignoreError(-2289, super.dropSequence(sequenceName)); + } + + @Override + public String alterTableDropConstraint(String tableName, String constraintName) { + return ignoreError(-2443, super.alterTableDropConstraint(tableName, constraintName)); + } + + @Override + public String alterTableDropUniqueConstraint(String tableName, String uniqueConstraintName) { + return ignoreError(-2443, super.alterTableDropUniqueConstraint(tableName, uniqueConstraintName)); + } + @Override protected void appendForeignKeyOnUpdate(StringBuilder buffer, ConstraintMode mode) { // do nothing, no on update clause for oracle @@ -57,4 +74,21 @@ public class OracleDdl extends PlatformDdl { return asIdentityStandardOptions(columnDefn, identity); } + /** + * generates anonymous pl/sql block that catches expected error so that we can + * effectively do a 'drop if exists' + */ + private String ignoreError(int errorNr, String statement) { + StringBuilder sb = new StringBuilder(); + sb.append("delimiter $$\n") + .append("declare\n") + .append(" expected_error exception;\n") + .append(" pragma exception_init(expected_error, ").append(errorNr).append(");\n") + .append("begin\n") + .append(" execute immediate '").append(statement).append("';\n") + .append("exception\n") + .append(" when expected_error then null;\n") + .append("end;\n$$"); + return sb.toString(); + } } diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/PlatformDdl.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/PlatformDdl.java index 34d7f7ebc..8156e67ae 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/PlatformDdl.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/PlatformDdl.java @@ -9,11 +9,7 @@ import io.ebean.config.dbplatform.DbDefaultValue; import io.ebean.config.dbplatform.DbIdentity; import io.ebean.config.dbplatform.IdType; import io.ebean.util.StringHelper; -import io.ebeaninternal.dbmigration.ddlgeneration.BaseDdlHandler; -import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer; -import io.ebeaninternal.dbmigration.ddlgeneration.DdlHandler; -import io.ebeaninternal.dbmigration.ddlgeneration.DdlOptions; -import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite; +import io.ebeaninternal.dbmigration.ddlgeneration.*; import io.ebeaninternal.dbmigration.ddlgeneration.platform.util.PlatformTypeConverter; import io.ebeaninternal.dbmigration.ddlgeneration.platform.util.VowelRemover; import io.ebeaninternal.dbmigration.migration.AddHistoryTable; @@ -22,8 +18,6 @@ import io.ebeaninternal.dbmigration.migration.Column; import io.ebeaninternal.dbmigration.migration.DropHistoryTable; import io.ebeaninternal.dbmigration.model.MTable; -import java.io.IOException; -import java.util.Collection; import java.util.List; import java.util.Locale; @@ -82,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"; @@ -102,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"; @@ -236,7 +226,7 @@ public class PlatformDdl { /** * Write all the table columns converting to platform types as necessary. */ - public void writeTableColumns(DdlBuffer apply, List columns, DdlIdentity identity) throws IOException { + public void writeTableColumns(DdlBuffer apply, List columns, DdlIdentity identity) { for (int i = 0; i < columns.size(); i++) { if (i > 0) { apply.append(","); @@ -260,7 +250,7 @@ public class PlatformDdl { /** * Write the column definition to the create table statement. */ - protected void writeColumnDefinition(DdlBuffer buffer, Column column, DdlIdentity identity) throws IOException { + protected void writeColumnDefinition(DdlBuffer buffer, Column column, DdlIdentity identity) { String columnDefn = convert(column.getType()); if (identity.useIdentity() && isTrue(column.isPrimaryKey())) { @@ -277,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) throws IOException { - buffer.append(" not null"); - } - /** * Returns the check constraint. */ @@ -319,9 +302,7 @@ public class PlatformDdl { if (type == null) { return null; } - type = extract(type); - if (type.contains("[]")) { return convertArrayType(type); } @@ -333,16 +314,18 @@ public class PlatformDdl { // e.g. @Column(columnDefinition = "db2;blob(64M);sqlserver,h2;varchar(227);varchar(127)") private String extract(String type) { String[] tmp = type.split(";"); - assert tmp.length % 2 == 1; - for (int i = 0; i < tmp.length - 2; i+=2) { + if (tmp.length % 2 == 0) { + throw new IllegalArgumentException("You need an odd number of arguments. See Issue #2559 for details"); + } + for (int i = 0; i < tmp.length - 2; i += 2) { String[] platforms = tmp[i].split(","); for (String plat : platforms) { if (platform.isPlatform(Platform.valueOf(plat.toUpperCase(Locale.ENGLISH)))) { - return tmp[i+1]; + return tmp[i + 1]; } } } - return tmp[tmp.length-1]; // else + return tmp[tmp.length - 1]; // else } /** @@ -359,29 +342,29 @@ public class PlatformDdl { /** * Add history support to this table using the platform specific mechanism. */ - public void createWithHistory(DdlWrite writer, MTable table) throws IOException { + public void createWithHistory(DdlWrite writer, MTable table) { historyDdl.createWithHistory(writer, table); } /** * Drop history support for a given table. */ - public void dropHistoryTable(DdlWrite writer, DropHistoryTable dropHistoryTable) throws IOException { + public void dropHistoryTable(DdlWrite writer, DropHistoryTable dropHistoryTable) { historyDdl.dropHistoryTable(writer, dropHistoryTable); } /** * Add history support to an existing table. */ - public void addHistoryTable(DdlWrite writer, AddHistoryTable addHistoryTable) throws IOException { + public void addHistoryTable(DdlWrite writer, AddHistoryTable addHistoryTable) { historyDdl.addHistoryTable(writer, addHistoryTable); } /** * Regenerate the history triggers (or function) due to a column being added/dropped/excluded or included. */ - public void regenerateHistoryTriggers(DdlWrite write, HistoryTableUpdate update) throws IOException { - historyDdl.updateTriggers(write, update); + public void regenerateHistoryTriggers(DdlWrite writer, String tableName) { + historyDdl.updateTriggers(writer, tableName); } /** @@ -550,13 +533,11 @@ public class PlatformDdl { return buffer.toString(); } - public void alterTableAddColumn(DdlBuffer buffer, String tableName, Column column, boolean onHistoryTable, String defaultValue) throws IOException { + 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) { @@ -568,28 +549,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) throws IOException { - 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); } /** @@ -604,22 +592,27 @@ public class PlatformDdl { /** * Alter a column type. *

- * Note that that MySql and SQL Server instead use alterColumnBaseAttributes() + * Note that that MySql, SQL Server, and HANA instead use alterColumn() *

*/ - 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. *

- * Note that that MySql, SQL Server, and HANA instead use alterColumnBaseAttributes() + * Note that that MySql, SQL Server, and HANA instead use alterColumn() *

*/ - 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); + } } /** @@ -631,22 +624,46 @@ public class PlatformDdl { /** * Alter column setting the default value. + *

+ * Note that that MySql, SQL Server, and HANA instead use alterColumn() + *

*/ - 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). *

- * 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. *

*/ - 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) { @@ -705,21 +722,21 @@ public class PlatformDdl { /** * Add an inline table comment to the create table statement. */ - public void inlineTableComment(DdlBuffer apply, String tableComment) throws IOException { + public void inlineTableComment(DdlBuffer apply, String tableComment) { // do nothing by default (MySql only) } /** * Add an table storage engine to the create table statement. */ - public void tableStorageEngine(DdlBuffer apply, String storageEngine) throws IOException { + public void tableStorageEngine(DdlBuffer apply, String storageEngine) { // do nothing by default } /** * Add table comment as a separate statement (from the create table statement). */ - public void addTableComment(DdlBuffer apply, String tableName, String tableComment) throws IOException { + public void addTableComment(DdlBuffer apply, String tableName, String tableComment) { if (DdlHelp.isDropComment(tableComment)) { tableComment = ""; } @@ -729,7 +746,7 @@ public class PlatformDdl { /** * Add column comment as a separate statement. */ - public void addColumnComment(DdlBuffer apply, String table, String column, String comment) throws IOException { + public void addColumnComment(DdlBuffer apply, String table, String column, String comment) { if (DdlHelp.isDropComment(comment)) { comment = ""; } @@ -739,14 +756,14 @@ public class PlatformDdl { /** * Use this to generate a prolog for each script (stored procedures) */ - public void generateProlog(DdlWrite write) throws IOException { + public void generateProlog(DdlWrite writer) { } /** * Use this to generate an epilog. Will be added at the end of script */ - public void generateEpilog(DdlWrite write) throws IOException { + public void generateEpilog(DdlWrite writer) { } @@ -769,20 +786,6 @@ public class PlatformDdl { return name; } - /** - * Mysql-specific: Locks all tables for triggers that have to be updated. - */ - public void lockTables(DdlBuffer buffer, Collection tables) throws IOException { - - } - - /** - * Mysql-specific: Unlocks all tables for triggers that have to be updated. - */ - public void unlockTables(DdlBuffer buffer, Collection tables) throws IOException { - - } - /** * Returns the database-specific "create table" command prefix. For HANA this is * either "create column table" or "create row table", for all other databases @@ -798,7 +801,7 @@ public class PlatformDdl { return false; } - public void addTablePartition(DdlBuffer apply, String partitionMode, String partitionColumn) throws IOException { + public void addTablePartition(DdlBuffer apply, String partitionMode, String partitionColumn) { // only supported by postgres initially } @@ -806,7 +809,7 @@ public class PlatformDdl { * Adds tablespace declaration. Now only supported for db2. * @throws IOException */ - public void addTablespace(DdlBuffer apply, String tablespaceName, String indexTablespace, String lobTablespace) throws IOException{ + public void addTablespace(DdlBuffer apply, String tablespaceName, String indexTablespace, String lobTablespace) { // now only supported for db2 } diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/PlatformHistoryDdl.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/PlatformHistoryDdl.java index c5e93aa23..4dd1dec6d 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/PlatformHistoryDdl.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/PlatformHistoryDdl.java @@ -6,8 +6,6 @@ import io.ebeaninternal.dbmigration.migration.AddHistoryTable; import io.ebeaninternal.dbmigration.migration.DropHistoryTable; import io.ebeaninternal.dbmigration.model.MTable; -import java.io.IOException; - /** * Defines the implementation for adding history support to a table. */ @@ -21,20 +19,32 @@ public interface PlatformHistoryDdl { /** * Creates a new table and add history support to the table using platform specific mechanism. */ - void createWithHistory(DdlWrite writer, MTable table) throws IOException; + void createWithHistory(DdlWrite writer, MTable table); /** * Drop history support for the given table. */ - void dropHistoryTable(DdlWrite writer, DropHistoryTable dropHistoryTable) throws IOException; + void dropHistoryTable(DdlWrite writer, DropHistoryTable dropHistoryTable); /** * Add history support to the given table. */ - void addHistoryTable(DdlWrite writer, AddHistoryTable addHistoryTable) throws IOException; + void addHistoryTable(DdlWrite writer, AddHistoryTable addHistoryTable); + + /** + * 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) + */ + 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. */ - void updateTriggers(DdlWrite write, HistoryTableUpdate baseTable) throws IOException; + default void updateTriggers(DdlWrite writer, String tableName) { + // nop + } } diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/PostgresDdl.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/PostgresDdl.java index 734621a66..1618cfd9e 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/PostgresDdl.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/PostgresDdl.java @@ -2,8 +2,8 @@ package io.ebeaninternal.dbmigration.ddlgeneration.platform; import io.ebean.config.dbplatform.DatabasePlatform; import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer; - -import java.io.IOException; +import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite; +import io.ebeaninternal.dbmigration.migration.AlterColumn; /** * Postgres specific DDL. @@ -40,7 +40,7 @@ public class PostgresDdl extends PlatformDdl { } @Override - public void addTablePartition(DdlBuffer apply, String partitionMode, String partitionColumn) throws IOException { + public void addTablePartition(DdlBuffer apply, String partitionMode, String partitionColumn) { apply.append(" partition by range (").append(partitionColumn).append(")"); } @@ -58,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); } } diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/PostgresHistoryDdl.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/PostgresHistoryDdl.java index b0257f96a..135324899 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/PostgresHistoryDdl.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/PostgresHistoryDdl.java @@ -4,7 +4,6 @@ import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer; import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite; import io.ebeaninternal.dbmigration.model.MTable; -import java.io.IOException; import java.util.List; /** @@ -21,7 +20,7 @@ public class PostgresHistoryDdl extends DbTriggerBasedHistoryDdl { * Use Postgres create table like to create the history table. */ @Override - protected void createHistoryTable(DdlBuffer apply, MTable table) throws IOException { + protected void createHistoryTable(DdlBuffer apply, MTable table) { apply.append("create table ").append(table.getName()).append(historySuffix) .append("(like ").append(table.getName()).append(")").endOfStatement(); } @@ -30,50 +29,45 @@ 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) throws IOException { - 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(); } } @Override - protected void appendSysPeriodColumns(DdlBuffer apply, String prefix) throws IOException { + protected void appendSysPeriodColumns(DdlBuffer apply, String prefix) { appendColumnName(apply, prefix, sysPeriod); } @Override - protected void dropSysPeriodColumns(DdlBuffer buffer, String baseTableName) throws IOException { - 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) throws IOException { - String baseTableName = table.getName(); + protected void createTriggers(DdlBuffer buffer, String baseTableName, List 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(); } @Override - protected void dropTriggers(DdlBuffer buffer, String baseTable) throws IOException { + protected void dropTriggers(DdlBuffer buffer, String baseTable) { // rollback trigger then function buffer.append("drop trigger if exists ").append(triggerName(baseTable)).append(" on ").append(baseTable).append(" cascade").endOfStatement(); buffer.append("drop function if exists ").append(procedureName(baseTable)).append("()").endOfStatement(); buffer.end(); } - protected void createOrReplaceFunction(DdlBuffer apply, String procedureName, String historyTable, List includedColumns) throws IOException { + protected void createOrReplaceFunction(DdlBuffer apply, String procedureName, String historyTable, List includedColumns) { apply .append("create or replace function ").append(procedureName).append("() returns trigger as $$").newLine(); @@ -105,23 +99,7 @@ public class PostgresHistoryDdl extends DbTriggerBasedHistoryDdl { } @Override - protected void createStoredFunction(DdlWrite writer, MTable table) throws IOException { - String procedureName = procedureName(table.getName()); - String historyTable = historyTableName(table.getName()); - - List columnNames = columnNamesForApply(table); - createOrReplaceFunction(writer.applyHistoryTrigger(), procedureName, historyTable, columnNames); - } - - @Override - protected void updateHistoryTriggers(DbTriggerUpdate update) throws IOException { - String procedureName = procedureName(update.getBaseTable()); - recreateHistoryView(update); - createOrReplaceFunction(update.historyTriggerBuffer(), procedureName, update.getHistoryTable(), update.getColumns()); - } - - @Override - protected void appendInsertIntoHistory(DdlBuffer buffer, String historyTable, List columns) throws IOException { + protected void appendInsertIntoHistory(DdlBuffer buffer, String historyTable, List columns) { buffer.append(" insert into ").append(historyTable).append(" (").append(sysPeriod).append(","); appendColumnNames(buffer, columns, ""); buffer.append(") values (tstzrange(lowerTs,upperTs), "); diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/SQLiteDdl.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/SQLiteDdl.java index 392051dac..50ce99d25 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/SQLiteDdl.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/SQLiteDdl.java @@ -1,11 +1,19 @@ 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; /** - * DB2 platform specific DDL. + * SQLite platform specific DDL. + * + * Note: SQLite has very limited alter capabilities. Altering a column is not + * supported and may need a recreation of the whole table */ public class SQLiteDdl extends PlatformDdl { @@ -27,7 +35,57 @@ public class SQLiteDdl extends PlatformDdl { @Override public String alterTableAddForeignKey(DdlOptions options, WriteForeignKey request) { - // not supported - return null; + return "-- not supported: " + super.alterTableAddForeignKey(options, request); + } + + @Override + public String alterTableDropForeignKey(String tableName, String fkName) { + return "-- not supported: " + super.alterTableDropForeignKey(tableName, fkName); + } + + @Override + public String alterTableAddCheckConstraint(String tableName, String checkConstraintName, String checkConstraint) { + return "-- not supported: " + super.alterTableAddCheckConstraint(tableName, checkConstraintName, checkConstraint); + } + + @Override + public String alterTableDropConstraint(String tableName, String constraintName) { + return "-- not supported: " + super.alterTableDropConstraint(tableName, constraintName); + } + + @Override + public String alterTableAddUniqueConstraint(String tableName, String uqName, String[] columns, String[] nullableColumns) { + return "-- not supported: " + super.alterTableAddUniqueConstraint(tableName, uqName, columns, nullableColumns); + } + + @Override + public String alterTableDropUniqueConstraint(String tableName, String uniqueConstraintName) { + return "-- not supported: " + super.alterTableDropUniqueConstraint(tableName, uniqueConstraintName); + } + + @Override + protected DdlAlterTable alterTable(DdlWrite writer, String tableName) { + return writer.applyAlterTable(tableName, SQLiteAlterTableWrite::new); + } + + static class SQLiteAlterTableWrite extends BaseAlterTableWrite { + public SQLiteAlterTableWrite(String tableName) { + super(tableName); + } + + @Override + protected List postProcessCommands(List cmds) { + List ret = new ArrayList(); + for (AlterCmd cmd : cmds) { + switch (cmd.getOperation()) { + case "alter column": + ret.add(newRawCommand("-- not supported: " + cmd)); + break; + default: + ret.add(cmd); + } + } + return ret; + } } } diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/SqlServerDdl.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/SqlServerDdl.java index 00edd1b75..d2dd88afa 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/SqlServerDdl.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/SqlServerDdl.java @@ -6,13 +6,17 @@ import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer; import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite; import io.ebeaninternal.dbmigration.migration.AlterColumn; -import java.io.IOException; - /** * MS SQL Server platform specific DDL. */ 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)"; @@ -33,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 @@ -48,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 @@ -73,12 +72,12 @@ public class SqlServerDdl extends PlatformDdl { throw new NullPointerException(); } // issues#233 - StringBuilder sb = new StringBuilder("create unique nonclustered index "); - sb.append(uqName).append(" on ").append(tableName).append('('); + 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]); } @@ -93,11 +92,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). */ @@ -105,10 +102,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. */ @@ -135,57 +134,60 @@ 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); + } + } } /** * Add table comment as a separate statement (from the create table statement). */ @Override - public void addTableComment(DdlBuffer apply, String tableName, String tableComment) throws IOException { + public void addTableComment(DdlBuffer apply, String tableName, String tableComment) { // do nothing for MS SQL Server (cause it requires stored procedures etc) } @@ -194,7 +196,7 @@ public class SqlServerDdl extends PlatformDdl { * Add column comment as a separate statement. */ @Override - public void addColumnComment(DdlBuffer apply, String table, String column, String comment) throws IOException { + public void addColumnComment(DdlBuffer apply, String table, String column, String comment) { // do nothing for MS SQL Server (cause it requires stored procedures etc) } @@ -204,47 +206,58 @@ 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) throws IOException { - - 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); } /** * This writes the multi value datatypes needed for MultiValueBind. */ @Override - public void generateProlog(DdlWrite write) throws IOException { - super.generateProlog(write); + public void generateProlog(DdlWrite writer) { + super.generateProlog(writer); - generateTVPDefinitions(write, "bigint"); - generateTVPDefinitions(write, "float"); - generateTVPDefinitions(write, "bit"); - generateTVPDefinitions(write, "date"); - generateTVPDefinitions(write, "time"); + generateTVPDefinitions(writer, "bigint"); + generateTVPDefinitions(writer, "float"); + generateTVPDefinitions(writer, "bit"); + generateTVPDefinitions(writer, "date"); + generateTVPDefinitions(writer, "time"); //generateTVPDefinitions(write, "datetime2"); - generateTVPDefinitions(write, "uniqueidentifier"); - generateTVPDefinitions(write, "nvarchar(max)"); + generateTVPDefinitions(writer, "uniqueidentifier"); + generateTVPDefinitions(writer, "nvarchar(max)"); } - private void generateTVPDefinitions(DdlWrite write, String definition) throws IOException { + private void generateTVPDefinitions(DdlWrite writer, String definition) { int pos = definition.indexOf('('); String name = pos == -1 ? definition : definition.substring(0, pos); - dropTVP(write.dropAll(), name); + dropTVP(writer.dropAll(), name); //TVPs are included in "I__create_procs.sql" //createTVP(write.apply(), name, definition); } - private void dropTVP(DdlBuffer ddl, String name) throws IOException { + private void dropTVP(DdlBuffer ddl, String name) { ddl.append("if exists (select name from sys.types where name = 'ebean_").append(name) .append("_tvp') drop type ebean_").append(name).append("_tvp").endOfStatement(); } - private void createTVP(DdlBuffer ddl, String name, String definition) throws IOException { + @SuppressWarnings("unused") + private void createTVP(DdlBuffer ddl, String name, String definition) { ddl.append("if not exists (select name from sys.types where name = 'ebean_").append(name) .append("_tvp') create type ebean_").append(name).append("_tvp as table (c1 ").append(definition).append(")") .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); + } } diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/SqlServerHistoryDdl.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/SqlServerHistoryDdl.java index 4b87ff04e..c0b1eaebc 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/SqlServerHistoryDdl.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/SqlServerHistoryDdl.java @@ -1,14 +1,14 @@ 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; -import java.io.IOException; - /** * @author Vilmos Nagy */ @@ -26,7 +26,7 @@ public class SqlServerHistoryDdl implements PlatformHistoryDdl { } @Override - public void createWithHistory(DdlWrite writer, MTable table) throws IOException { + public void createWithHistory(DdlWrite writer, MTable table) { String baseTable = table.getName(); enableSystemVersioning(writer, baseTable); } @@ -43,8 +43,8 @@ public class SqlServerHistoryDdl implements PlatformHistoryDdl { return historyTable; } - private void enableSystemVersioning(DdlWrite writer, String baseTable) throws IOException { - DdlBuffer apply = writer.applyHistoryView(); + private void enableSystemVersioning(DdlWrite writer, String baseTable) { + 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,40 +59,49 @@ public class SqlServerHistoryDdl implements PlatformHistoryDdl { } @Override - public void dropHistoryTable(DdlWrite writer, DropHistoryTable dropHistoryTable) throws IOException { + 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 - public void addHistoryTable(DdlWrite writer, AddHistoryTable addHistoryTable) throws IOException { + public void addHistoryTable(DdlWrite writer, AddHistoryTable addHistoryTable) { String baseTable = addHistoryTable.getBaseTable(); enableSystemVersioning(writer, baseTable); } @Override - public void updateTriggers(DdlWrite writer, HistoryTableUpdate baseTable) throws IOException { - // SQL Server 2016 does not need triggers - DdlBuffer apply = writer.applyHistoryView(); - String baseTableName = baseTable.getBaseTable(); - apply.append("-- alter table ").append(baseTableName).append(" set (system_versioning = off (history_table=") - .append(getHistoryTable(baseTableName)).append("))").endOfStatement(); - apply.append("-- history migration goes here").newLine(); - apply.append("-- alter table ").append(baseTableName).append(" set (system_versioning = on (history_table=") - .append(getHistoryTable(baseTableName)).append("))").endOfStatement(); - + 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(); } + } diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/YugabyteHistoryDdl.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/YugabyteHistoryDdl.java index 3ba023881..204dcd762 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/YugabyteHistoryDdl.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/YugabyteHistoryDdl.java @@ -3,12 +3,10 @@ package io.ebeaninternal.dbmigration.ddlgeneration.platform; import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer; import io.ebeaninternal.dbmigration.model.MTable; -import java.io.IOException; - public class YugabyteHistoryDdl extends PostgresHistoryDdl { @Override - protected void createHistoryTable(DdlBuffer apply, MTable table) throws IOException { + protected void createHistoryTable(DdlBuffer apply, MTable table) { createHistoryTableAs(apply, table); writeColumnDefinition(apply, sysPeriod, "tstzrange"); apply.newLine().append(")").endOfStatement(); diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/CurrentModel.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/CurrentModel.java index e5c8743d2..faac168ea 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/CurrentModel.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/CurrentModel.java @@ -36,7 +36,7 @@ public class CurrentModel { private ModelContainer model; private ChangeSet changeSet; - private DdlWrite write; + private DdlWrite writer; /** * Construct with a given EbeanServer instance for DDL create all generation, not migration. @@ -117,7 +117,7 @@ public class CurrentModel { /** * Return the 'Create' DDL. */ - public String getCreateDdl() throws IOException { + public String getCreateDdl() { createDdl(); @@ -128,10 +128,13 @@ public class CurrentModel { if (jaxbPresent) { addExtraDdl(ddl, ExtraDdlXmlReader.readBuiltin(), "-- init script "); } - ddl.append(write.apply().getBuffer()); - ddl.append(write.applyForeignKeys().getBuffer()); - ddl.append(write.applyHistoryView().getBuffer()); - ddl.append(write.applyHistoryTrigger().getBuffer()); + + try { + writer.writeApply(ddl); + } catch (IOException e) { // should not happen on StringBuilder + throw new RuntimeException(e); + } + return ddl.toString(); } @@ -150,7 +153,7 @@ public class CurrentModel { /** * Return the 'Drop' DDL. */ - public String getDropAllDdl() throws IOException { + public String getDropAllDdl() { createDdl(); @@ -158,22 +161,25 @@ public class CurrentModel { if (ddlHeader != null && !ddlHeader.isEmpty()) { ddl.append(ddlHeader).append('\n'); } - ddl.append(write.dropAllForeignKeys().getBuffer()); - ddl.append(write.dropAll().getBuffer()); + try { + writer.writeDropAll(ddl); + } catch (IOException e) { // should not happen on StringBuilder + throw new RuntimeException(e); + } return ddl.toString(); } /** * Create all the DDL based on the changeSet. */ - private void createDdl() throws IOException { - if (write == null) { + private void createDdl() { + if (writer == null) { ChangeSet createChangeSet = getChangeSet(); - write = new DdlWrite(new MConfiguration(), model, ddlOptions); + writer = new DdlWrite(new MConfiguration(), model, ddlOptions); DdlHandler handler = handler(); - handler.generateProlog(write); - handler.generate(write, createChangeSet); - handler.generateEpilog(write); + handler.generateProlog(writer); + handler.generate(writer, createChangeSet); + handler.generateEpilog(writer); } } diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/MTableIdentity.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/MTableIdentity.java index f226ebaec..aea2425d8 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/MTableIdentity.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/MTableIdentity.java @@ -89,8 +89,9 @@ public class MTableIdentity { return IdentityType.EXTERNAL; case GENERATOR: return IdentityType.GENERATOR; + default: + return null; } - return null; } private static int toInt(BigInteger firstVal, BigInteger secVal) { diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/PlatformDdlWriter.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/PlatformDdlWriter.java index ec55dd9a9..83de7e459 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/PlatformDdlWriter.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/PlatformDdlWriter.java @@ -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; @@ -39,23 +38,24 @@ public class PlatformDdlWriter { /** * Write the migration as platform specific ddl. */ - public void processMigration(Migration dbMigration, DdlWrite write, File writePath, String fullVersion) throws IOException { + public void processMigration(Migration dbMigration, DdlWrite writer, File writePath, String fullVersion) + throws IOException { DdlHandler handler = handler(); - handler.generateProlog(write); + handler.generateProlog(writer); if (lockTimeoutSeconds > 0) { String lockSql = platformDdl.setLockTimeout(lockTimeoutSeconds); if (lockSql != null) { - write.apply().append(lockSql).endOfStatement().newLine(); + writer.apply().append(lockSql).endOfStatement().newLine(); } } List changeSets = dbMigration.getChangeSet(); for (ChangeSet changeSet : changeSets) { if (isApply(changeSet)) { - handler.generate(write, changeSet); + handler.generate(writer, changeSet); } } - handler.generateEpilog(write); - writePlatformDdl(write, writePath, fullVersion); + handler.generateEpilog(writer); + writePlatformDdl(writer, writePath, fullVersion); } /** @@ -68,10 +68,10 @@ public class PlatformDdlWriter { /** * Write the ddl files. */ - protected void writePlatformDdl(DdlWrite write, File resourcePath, String fullVersion) throws IOException { - if (!write.isApplyEmpty()) { + protected void writePlatformDdl(DdlWrite writer, File resourcePath, String fullVersion) throws IOException { + if (!writer.isApplyEmpty()) { try (Writer applyWriter = createWriter(resourcePath, fullVersion, ".sql")) { - writeApplyDdl(applyWriter, write); + writeApplyDdl(applyWriter, writer); applyWriter.flush(); } } @@ -85,26 +85,12 @@ public class PlatformDdlWriter { /** * Write the 'Apply' DDL buffers to the writer. */ - protected void writeApplyDdl(Writer writer, DdlWrite write) throws IOException { + protected void writeApplyDdl(Writer writer, DdlWrite ddl) throws IOException { String header = databaseConfig.getDdlHeader(); if (header != null && !header.isEmpty()) { writer.append(header).append('\n'); } - // merge the apply buffers in the appropriate order - prependDropDependencies(writer, write.applyDropDependencies()); - writer.append("-- apply changes\n"); - writer.append(write.apply().getBuffer()); - writer.append(write.applyForeignKeys().getBuffer()); - writer.append(write.applyHistoryView().getBuffer()); - writer.append(write.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); } /** diff --git a/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/ddlgeneration/BaseDdlHandlerTest.java b/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/ddlgeneration/BaseDdlHandlerTest.java index 550d508ad..092bc75fa 100644 --- a/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/ddlgeneration/BaseDdlHandlerTest.java +++ b/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/ddlgeneration/BaseDdlHandlerTest.java @@ -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) { @@ -45,192 +45,196 @@ public class BaseDdlHandlerTest extends BaseTestCase { @Test public void addColumn_nullable_noConstraint() throws Exception { - DdlWrite write = new DdlWrite(); - h2Handler().generate(write, Helper.getAddColumn()); - assertThat(write.apply().getBuffer()).isEqualTo("alter table foo add column added_to_foo varchar(20);\n\n"); + DdlWrite writer = new DdlWrite(); + h2Handler().generate(writer, Helper.getAddColumn()); + assertThat(writer.toString()).isEqualTo("-- apply alter tables\nalter table foo add column added_to_foo varchar(20);\n"); - write = new DdlWrite(); - sqlserverHandler().generate(write, Helper.getAddColumn()); - assertThat(write.apply().getBuffer()).isEqualTo("alter table foo add added_to_foo nvarchar(20);\n\n"); + writer = new DdlWrite(); + sqlserverHandler().generate(writer, Helper.getAddColumn()); + assertThat(writer.toString()).isEqualTo("-- apply alter tables\nalter table foo add added_to_foo nvarchar(20);\n"); - write = new DdlWrite(); - hanaHandler().generate(write, Helper.getAddColumn()); - assertThat(write.apply().getBuffer()).isEqualTo("alter table foo add ( added_to_foo nvarchar(20));\n\n"); + writer = new DdlWrite(); + hanaHandler().generate(writer, Helper.getAddColumn()); + assertThat(writer.toString()).isEqualTo("-- apply alter tables\nalter table foo add (added_to_foo nvarchar(20));\n"); } @Test public void addColumn_withCheckConstraint() throws Exception { - DdlWrite write = new DdlWrite(); - h2Handler().generate(write, Helper.getAlterTableAddColumnWithCheckConstraint()); - assertThat(write.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"); + DdlWrite writer = new DdlWrite(); + h2Handler().generate(writer, Helper.getAlterTableAddColumnWithCheckConstraint()); + 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"); - write = new DdlWrite(); - hanaHandler().generate(write, Helper.getAlterTableAddColumnWithCheckConstraint()); - assertThat(write.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"); + writer = new DdlWrite(); + hanaHandler().generate(writer, Helper.getAlterTableAddColumnWithCheckConstraint()); + 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 { - DdlWrite write = new DdlWrite(); + DdlWrite writer = new DdlWrite(); DdlHandler postgresHandler = postgresHandler(); - postgresHandler.generate(write, Helper.getAlterTableAddDbArrayColumn()); + postgresHandler.generate(writer, Helper.getAlterTableAddDbArrayColumn()); - assertThat(write.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"); - write = new DdlWrite(); + writer = new DdlWrite(); DdlHandler sqlserverHandler = sqlserverHandler(); - sqlserverHandler.generate(write, Helper.getAlterTableAddDbArrayColumn()); - assertThat(write.apply().getBuffer()).isEqualTo("alter table foo add dbarray_added_to_foo varchar(1000);\n\n"); + sqlserverHandler.generate(writer, Helper.getAlterTableAddDbArrayColumn()); + assertThat(writer.toString()).isEqualTo("-- apply alter tables\nalter table foo add dbarray_added_to_foo varchar(1000);\n"); - write = new DdlWrite(); + writer = new DdlWrite(); DdlHandler hanaHandler = hanaHandler(); - hanaHandler.generate(write, Helper.getAlterTableAddDbArrayColumn()); - assertThat(write.apply().getBuffer()).isEqualTo("alter table foo add ( dbarray_added_to_foo nvarchar(255) array);\n\n"); + hanaHandler.generate(writer, Helper.getAlterTableAddDbArrayColumn()); + assertThat(writer.toString()) + .isEqualTo("-- apply alter tables\nalter table foo add (dbarray_added_to_foo nvarchar(255) array);\n"); } @Test public void addColumn_dbarray_withLength() throws Exception { - DdlWrite write = new DdlWrite(); + DdlWrite writer = new DdlWrite(); - postgresHandler().generate(write, Helper.getAlterTableAddDbArrayColumnWithLength()); - assertThat(write.apply().getBuffer()).isEqualTo("alter table foo add column dbarray_ninety varchar[];\n\n"); + postgresHandler().generate(writer, Helper.getAlterTableAddDbArrayColumnWithLength()); + assertThat(writer.toString()).isEqualTo("-- apply alter tables\nalter table foo add column dbarray_ninety varchar[];\n"); - write = new DdlWrite(); - h2Handler().generate(write, Helper.getAlterTableAddDbArrayColumnWithLength()); + writer = new DdlWrite(); + h2Handler().generate(writer, Helper.getAlterTableAddDbArrayColumnWithLength()); if (useV1Syntax) { - assertThat(write.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(write.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"); } - write = new DdlWrite(); - sqlserverHandler().generate(write, Helper.getAlterTableAddDbArrayColumnWithLength()); - assertThat(write.apply().getBuffer()).isEqualTo("alter table foo add dbarray_ninety varchar(90);\n\n"); + writer = new DdlWrite(); + sqlserverHandler().generate(writer, Helper.getAlterTableAddDbArrayColumnWithLength()); + assertThat(writer.toString()).isEqualTo("-- apply alter tables\nalter table foo add dbarray_ninety varchar(90);\n"); - write = new DdlWrite(); - hanaHandler().generate(write, Helper.getAlterTableAddDbArrayColumnWithLength()); - assertThat(write.apply().getBuffer()).isEqualTo("alter table foo add ( dbarray_ninety nvarchar(255) array(90));\n\n"); + writer = new DdlWrite(); + hanaHandler().generate(writer, Helper.getAlterTableAddDbArrayColumnWithLength()); + assertThat(writer.toString()) + .isEqualTo("-- apply alter tables\nalter table foo add (dbarray_ninety nvarchar(255) array(90));\n"); } @Test public void addColumn_dbarray_integer_withLength() throws Exception { - DdlWrite write = new DdlWrite(); - postgresHandler().generate(write, Helper.getAlterTableAddDbArrayColumnIntegerWithLength()); - assertThat(write.apply().getBuffer()).isEqualTo("alter table foo add column dbarray_integer integer[];\n\n"); + DdlWrite writer = new DdlWrite(); + postgresHandler().generate(writer, Helper.getAlterTableAddDbArrayColumnIntegerWithLength()); + assertThat(writer.toString()).isEqualTo("-- apply alter tables\nalter table foo add column dbarray_integer integer[];\n"); - write = new DdlWrite(); - h2Handler().generate(write, Helper.getAlterTableAddDbArrayColumnIntegerWithLength()); + writer = new DdlWrite(); + h2Handler().generate(writer, Helper.getAlterTableAddDbArrayColumnIntegerWithLength()); if (useV1Syntax) { - assertThat(write.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(write.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"); } - write = new DdlWrite(); - sqlserverHandler().generate(write, Helper.getAlterTableAddDbArrayColumnIntegerWithLength()); - assertThat(write.apply().getBuffer()).isEqualTo("alter table foo add dbarray_integer varchar(90);\n\n"); + writer = new DdlWrite(); + sqlserverHandler().generate(writer, Helper.getAlterTableAddDbArrayColumnIntegerWithLength()); + assertThat(writer.toString()).isEqualTo("-- apply alter tables\nalter table foo add dbarray_integer varchar(90);\n"); - write = new DdlWrite(); - sqlserverHandler().generate(write, Helper.getAlterTableAddDbArrayColumnInteger()); - assertThat(write.apply().getBuffer()).isEqualTo("alter table foo add dbarray_integer varchar(1000);\n\n"); + writer = new DdlWrite(); + sqlserverHandler().generate(writer, Helper.getAlterTableAddDbArrayColumnInteger()); + assertThat(writer.toString()).isEqualTo("-- apply alter tables\nalter table foo add dbarray_integer varchar(1000);\n"); - write = new DdlWrite(); - hanaHandler().generate(write, Helper.getAlterTableAddDbArrayColumnIntegerWithLength()); - assertThat(write.apply().getBuffer()).isEqualTo("alter table foo add ( dbarray_integer integer array(90));\n\n"); + writer = new DdlWrite(); + hanaHandler().generate(writer, Helper.getAlterTableAddDbArrayColumnIntegerWithLength()); + assertThat(writer.toString()).isEqualTo("-- apply alter tables\nalter table foo add (dbarray_integer integer array(90));\n"); - write = new DdlWrite(); - hanaHandler().generate(write, Helper.getAlterTableAddDbArrayColumnInteger()); - assertThat(write.apply().getBuffer()).isEqualTo("alter table foo add ( dbarray_integer integer array);\n\n"); + writer = new DdlWrite(); + hanaHandler().generate(writer, Helper.getAlterTableAddDbArrayColumnInteger()); + assertThat(writer.toString()).isEqualTo("-- apply alter tables\nalter table foo add (dbarray_integer integer array);\n"); } @Test public void addColumn_withForeignKey() throws Exception { - DdlWrite write = new DdlWrite(); + DdlWrite writer = new DdlWrite(); DdlHandler handler = h2Handler(); - handler.generate(write, Helper.getAlterTableAddColumn()); + handler.generate(writer, Helper.getAlterTableAddColumn()); - String buffer = write.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 = write.applyForeignKeys().getBuffer(); + 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(write.dropAll().getBuffer()).isEqualTo(""); + assertThat(writer.dropAll().getBuffer()).isEqualTo(""); } @Test public void dropColumn() throws Exception { - DdlWrite write = new DdlWrite(); + DdlWrite writer = new DdlWrite(); DdlHandler handler = h2Handler(); - handler.generate(write, Helper.getDropColumn()); + handler.generate(writer, Helper.getDropColumn()); - assertThat(write.apply().getBuffer()).isEqualTo("alter table foo drop column col2;\n\n"); - assertThat(write.dropAll().getBuffer()).isEqualTo(""); + assertThat(writer.toString()).isEqualTo("-- apply alter tables\nalter table foo drop column col2;\n"); + assertThat(writer.dropAll().getBuffer()).isEqualTo(""); - write = new DdlWrite(); + writer = new DdlWrite(); DdlHandler hanaHandler = hanaHandler(); - hanaHandler.generate(write, Helper.getDropColumn()); + hanaHandler.generate(writer, Helper.getDropColumn()); - assertThat(write.apply().getBuffer()).isEqualTo("CALL usp_ebean_drop_column('foo', 'col2');\n\n"); - assertThat(write.dropAll().getBuffer()).isEqualTo(""); + assertThat(writer.toString()).isEqualTo("-- apply alter tables\nCALL usp_ebean_drop_column('foo', 'col2');\n"); + assertThat(writer.dropAll().getBuffer()).isEqualTo(""); } @Test public void createTable() throws Exception { - DdlWrite write = new DdlWrite(); + DdlWrite writer = new DdlWrite(); DdlHandler handler = h2Handler(); - handler.generate(write, Helper.getCreateTable()); + 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(write.apply().getBuffer()).isEqualTo(createTableDDL); - assertThat(write.dropAll().getBuffer().trim()).isEqualTo("drop table if exists foo;"); + assertThat(writer.toString()).isEqualTo(createTableDDL); + assertThat(writer.dropAll().getBuffer().trim()).isEqualTo("drop table if exists foo;"); - write = new DdlWrite(); + writer = new DdlWrite(); DdlHandler hanaHandler = hanaHandler(); - hanaHandler.generate(write, Helper.getCreateTable()); + 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(write.apply().getBuffer()).isEqualTo(createColumnTableDDL); - assertThat(write.dropAll().getBuffer().trim()).isEqualTo("drop table foo cascade;"); + assertThat(writer.toString()).isEqualTo(createColumnTableDDL); + assertThat(writer.dropAll().getBuffer().trim()).isEqualTo("drop table foo cascade;"); } @Test public void generateChangeSet() throws Exception { - DdlWrite write = new DdlWrite(); + DdlWrite writer = new DdlWrite(); DdlHandler handler = h2Handler(); - handler.generate(write, Helper.getChangeSet()); + handler.generate(writer, Helper.getChangeSet()); String apply = Helper.asText(this, "/assert/BaseDdlHandlerTest/baseApply.sql"); String rollbackLast = Helper.asText(this, "/assert/BaseDdlHandlerTest/baseDropAll.sql"); - - assertThat(write.apply().getBuffer()).isEqualTo(apply); - assertThat(write.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 @@ -241,16 +245,16 @@ public class BaseDdlHandlerTest extends BaseTestCase { ChangeSet createChangeSet = new CurrentModel(defaultServer).getChangeSet(); - DdlWrite write = new DdlWrite(); + DdlWrite writer = new DdlWrite(); DdlHandler handler = h2Handler(); - handler.generate(write, createChangeSet); + handler.generate(writer, createChangeSet); String apply = Helper.asText(this, "/assert/changeset-apply.txt"); String rollbackLast = Helper.asText(this, "/assert/changeset-dropAll.txt"); - assertThat(write.apply().getBuffer()).isEqualTo(apply); - assertThat(write.dropAll().getBuffer()).isEqualTo(rollbackLast); + assertThat(writer.toString()).isEqualTo(apply); + assertThat(writer.dropAll().getBuffer()).isEqualTo(rollbackLast); } @Disabled @@ -260,20 +264,20 @@ public class BaseDdlHandlerTest extends BaseTestCase { ChangeSet createChangeSet = new CurrentModel(defaultServer).getChangeSet(); - DdlWrite write = new DdlWrite(); + DdlWrite writer = new DdlWrite(); DdlHandler handler = postgresHandler(); - handler.generate(write, createChangeSet); + handler.generate(writer, createChangeSet); String apply = Helper.asText(this, "/assert/changeset-pg-apply.sql"); String applyLast = Helper.asText(this, "/assert/changeset-pg-applyLast.sql"); String rollbackFirst = Helper.asText(this, "/assert/changeset-pg-rollbackFirst.sql"); String rollbackLast = Helper.asText(this, "/assert/changeset-pg-rollbackLast.sql"); - assertThat(write.apply().getBuffer()).isEqualTo(apply); - assertThat(write.applyForeignKeys().getBuffer()).isEqualTo(applyLast); - assertThat(write.dropAllForeignKeys().getBuffer()).isEqualTo(rollbackFirst); - assertThat(write.dropAll().getBuffer()).isEqualTo(rollbackLast); + assertThat(writer.toString()).isEqualTo(apply); + assertThat(writer.applyForeignKeys().getBuffer()).isEqualTo(applyLast); + assertThat(writer.dropAllForeignKeys().getBuffer()).isEqualTo(rollbackFirst); + assertThat(writer.dropAll().getBuffer()).isEqualTo(rollbackLast); } } diff --git a/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/BaseTableDdlTest.java b/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/BaseTableDdlTest.java index d6af52969..3f09718f6 100644 --- a/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/BaseTableDdlTest.java +++ b/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/BaseTableDdlTest.java @@ -15,7 +15,6 @@ import io.ebeaninternal.dbmigration.migration.Column; import io.ebeaninternal.dbmigration.migration.CreateTable; import org.junit.jupiter.api.Test; -import java.io.IOException; import java.util.List; import static org.assertj.core.api.Assertions.assertThat; @@ -28,121 +27,121 @@ public class BaseTableDdlTest { private final PlatformDdl h2ddl = PlatformDdlBuilder.create(new H2Platform()); @Test - public void testAlterColumn() throws IOException { + public void testAlterColumn() { BaseTableDdl ddlGen = new BaseTableDdl(serverConfig, h2ddl); - DdlWrite write = new DdlWrite(); + DdlWrite writer = new DdlWrite(); AlterColumn alterColumn = new AlterColumn(); alterColumn.setTableName("mytab"); alterColumn.setCheckConstraint("check (acol in ('A','B'))"); alterColumn.setCheckConstraintName("ck_mytab_acol"); - ddlGen.generate(write, alterColumn); + ddlGen.generate(writer, alterColumn); - String ddl = write.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'))"); } @Test - public void testAddColumn_withTypeConversion() throws IOException { + public void testAddColumn_withTypeConversion() { BaseTableDdl ddlGen = new BaseTableDdl(serverConfig, PlatformDdlBuilder.create(new OraclePlatform())); - DdlWrite write = new DdlWrite(); + DdlWrite writer = new DdlWrite(); Column column = new Column(); column.setName("col_name"); column.setType("varchar(20)"); - ddlGen.alterTableAddColumn(write.apply(), "mytable", column, false, false); + ddlGen.alterTableAddColumn(writer, "mytable", column, false, false); - String ddl = write.apply().getBuffer(); + String ddl = writer.toString(); assertThat(ddl).contains("alter table mytable add col_name varchar2(20)"); } @Test - public void testAddColumn_withTypeConversion_clickHouseVarchar() throws IOException { + public void testAddColumn_withTypeConversion_clickHouseVarchar() { ClickHouseTableDdl ddlGen = new ClickHouseTableDdl(serverConfig, PlatformDdlBuilder.create(new ClickHousePlatform())); - DdlWrite write = new DdlWrite(); + DdlWrite writer = new DdlWrite(); Column column = new Column(); column.setName("col_name"); column.setType("varchar(20)"); - ddlGen.alterTableAddColumn(write.apply(), "mytable", column, false, false); + ddlGen.alterTableAddColumn(writer, "mytable", column, false, false); - String ddl = write.apply().getBuffer(); + String ddl = writer.toString(); assertThat(ddl).contains("alter table mytable add column col_name String"); } @Test - public void testAlterColumnComment() throws IOException { + public void testAlterColumnComment() { BaseTableDdl ddlGen = new BaseTableDdl(serverConfig, h2ddl); - DdlWrite write = new DdlWrite(); + DdlWrite writer = new DdlWrite(); AlterColumn alterColumn = new AlterColumn(); alterColumn.setTableName("mytab"); alterColumn.setColumnName("acol"); alterColumn.setComment("my comment"); - ddlGen.generate(write, alterColumn); + ddlGen.generate(writer, alterColumn); - String ddl = write.apply().getBuffer(); + String ddl = writer.applyPostAlter().getBuffer(); assertThat(ddl).contains("comment on column mytab.acol is 'my comment'"); } @Test - public void alterTableAddColumnWithComment() throws IOException { + public void alterTableAddColumnWithComment() { BaseTableDdl ddl = new BaseTableDdl(serverConfig, h2ddl); - DdlWrite write = new DdlWrite(); + DdlWrite writer = new DdlWrite(); Column column = new Column(); column.setName("my_column"); column.setComment("some comment"); column.setType("int"); - ddl.alterTableAddColumn(write.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", write.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 - public void testAddTableComment() throws IOException { + public void testAddTableComment() { BaseTableDdl ddlGen = new BaseTableDdl(serverConfig, h2ddl); - DdlWrite write = new DdlWrite(); + DdlWrite writer = new DdlWrite(); AddTableComment addTableComment = new AddTableComment(); addTableComment.setName("mytab"); addTableComment.setComment("my comment"); - ddlGen.generate(write, addTableComment); + ddlGen.generate(writer, addTableComment); - String ddl = write.apply().getBuffer(); + String ddl = writer.applyPostAlter().getBuffer(); assertThat(ddl).contains("comment on table mytab is 'my comment'"); } @Test - public void testAddTableComment_mysql() throws IOException { + public void testAddTableComment_mysql() { BaseTableDdl ddlGen = new BaseTableDdl(serverConfig, PlatformDdlBuilder.create(new MySqlPlatform())); - DdlWrite write = new DdlWrite(); + DdlWrite writer = new DdlWrite(); AddTableComment addTableComment = new AddTableComment(); addTableComment.setName("mytab"); addTableComment.setComment("my comment"); - ddlGen.generate(write, addTableComment); + ddlGen.generate(writer, addTableComment); - String ddl = write.apply().getBuffer(); + String ddl = writer.applyPostAlter().getBuffer(); assertThat(ddl).contains("alter table mytab comment = 'my comment'"); } @@ -151,14 +150,14 @@ public class BaseTableDdlTest { BaseTableDdl ddlGen = new BaseTableDdl(serverConfig, h2ddl); - DdlWrite write = new DdlWrite(); + DdlWrite writer = new DdlWrite(); - ddlGen.generate(write, createTable()); - String apply = write.apply().getBuffer(); - String applyLast = write.applyForeignKeys().getBuffer(); + ddlGen.generate(writer, createTable()); + String apply = writer.apply().getBuffer(); + String applyLast = writer.applyForeignKeys().getBuffer(); - String rollbackFirst = write.dropAllForeignKeys().getBuffer(); - String rollbackLast = write.dropAll().getBuffer(); + String rollbackFirst = writer.dropAllForeignKeys().getBuffer(); + String rollbackLast = writer.dropAll().getBuffer(); assertThat(apply).isEqualTo(Helper.asText(this, "/assert/BaseTableDdlTest/createTable-apply.txt")); assertThat(applyLast).isEqualTo(Helper.asText(this, "/assert/BaseTableDdlTest/createTable-applyLast.txt")); diff --git a/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/H2HistoryDdlTest.java b/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/H2HistoryDdlTest.java deleted file mode 100644 index a23a7ad79..000000000 --- a/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/H2HistoryDdlTest.java +++ /dev/null @@ -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 write = new DdlWrite(new MConfiguration(), modelContainer, new DdlOptions()); - - H2Platform h2Platform = new H2Platform(); - PlatformDdl h2Ddl = PlatformDdlBuilder.create(h2Platform); - h2Ddl.configure(ebeanServer.config()); - h2Ddl.regenerateHistoryTriggers(write, update); - - assertThat(write.applyHistoryView().isEmpty()).isFalse(); - assertThat(write.applyHistoryTrigger().isEmpty()).isFalse(); - assertThat(write.applyHistoryView().getBuffer()) - .contains("create view") - .doesNotContain("create trigger"); - assertThat(write.applyHistoryTrigger().getBuffer()) - .contains("add one") - .contains("create trigger") - .doesNotContain("create view"); - assertThat(write.dropAll().isEmpty()).isTrue(); - - } -} diff --git a/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/HanaDdlTest.java b/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/HanaDdlTest.java index 3e467babe..17a904aca 100644 --- a/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/HanaDdlTest.java +++ b/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/HanaDdlTest.java @@ -5,24 +5,22 @@ import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite; import io.ebeaninternal.dbmigration.migration.Column; import org.junit.jupiter.api.Test; -import java.io.IOException; - import static org.junit.jupiter.api.Assertions.assertEquals; public class HanaDdlTest { @Test - public void alterTableDropColumn() throws IOException { + public void alterTableDropColumn() { HanaColumnStoreDdl ddl = new HanaColumnStoreDdl(new HanaPlatform()); - DdlWrite write = new DdlWrite(); - ddl.alterTableDropColumn(write.apply(), "my_table", "my_column"); - assertEquals("CALL usp_ebean_drop_column('my_table', 'my_column');\n", write.apply().getBuffer()); + DdlWrite writer = new DdlWrite(); + ddl.alterTableDropColumn(writer, "my_table", "my_column"); + assertEquals("-- apply alter tables\nCALL usp_ebean_drop_column('my_table', 'my_column');\n", writer.toString()); } @Test - public void alterTableAddColumn() throws IOException { + public void alterTableAddColumn() { HanaColumnStoreDdl ddl = new HanaColumnStoreDdl(new HanaPlatform()); - DdlWrite write = new DdlWrite(); + DdlWrite writer = new DdlWrite(); Column column = new Column(); column.setName("my_column"); column.setComment("comment"); @@ -35,7 +33,9 @@ public class HanaDdlTest { column.setCheckConstraintName("check_constraint"); column.setHistoryExclude(Boolean.TRUE); column.setIdentity(Boolean.TRUE); - ddl.alterTableAddColumn(write.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", write.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()); } } diff --git a/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/HistoryTableUpdateTest.java b/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/HistoryTableUpdateTest.java deleted file mode 100644 index a2f2c6080..000000000 --- a/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/HistoryTableUpdateTest.java +++ /dev/null @@ -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]"); - } - -} diff --git a/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/PlatformDdl_AlterColumnTest.java b/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/PlatformDdl_AlterColumnTest.java index 2ae1118f1..c688a669a 100644 --- a/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/PlatformDdl_AlterColumnTest.java +++ b/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/PlatformDdl_AlterColumnTest.java @@ -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,14 +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 java.io.IOException; - -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 { @@ -33,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 drop default", 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() throws IOException { - DdlWrite write = new DdlWrite(); - oraDdl.alterTableAddColumn(write.apply(), "my_table", simpleColumn(), false, "1"); - assertThat(write.apply().getBuffer()).isEqualTo("alter table my_table add my_column int default 1 not null;\n"); + public void oracle_alterTableAddColumn() { + DdlWrite writer = new DdlWrite(); + 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() { @@ -269,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(); } diff --git a/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/PlatformDdl_dropUniqueConstraintTest.java b/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/PlatformDdl_dropUniqueConstraintTest.java index 00a6e271d..cebab093f 100644 --- a/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/PlatformDdl_dropUniqueConstraintTest.java +++ b/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/PlatformDdl_dropUniqueConstraintTest.java @@ -29,11 +29,20 @@ public class PlatformDdl_dropUniqueConstraintTest { sql = pgDdl.alterTableDropUniqueConstraint("mytab", "uq_name"); assertEquals("alter table mytab drop constraint uq_name", sql); sql = oraDdl.alterTableDropUniqueConstraint("mytab", "uq_name"); - assertEquals("alter table mytab drop constraint uq_name", sql); + assertEquals("delimiter $$\n" + + "declare\n" + + " expected_error exception;\n" + + " pragma exception_init(expected_error, -2443);\n" + + "begin\n" + + " execute immediate 'alter table mytab drop constraint uq_name';\n" + + "exception\n" + + " when expected_error then null;\n" + + "end;\n" + + "$$", sql); 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"); diff --git a/ebean-ddl-generator/src/test/java/io/localtest/BaseTestCase.java b/ebean-ddl-generator/src/test/java/io/localtest/BaseTestCase.java index b50932ac4..22b03d983 100644 --- a/ebean-ddl-generator/src/test/java/io/localtest/BaseTestCase.java +++ b/ebean-ddl-generator/src/test/java/io/localtest/BaseTestCase.java @@ -5,14 +5,11 @@ import io.ebean.Database; import io.ebean.annotation.PersistBatch; import io.ebean.annotation.Platform; import io.ebeaninternal.api.SpiEbeanServer; -import org.junit.jupiter.api.extension.ExtendWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.sql.Types; -import static org.assertj.core.api.Assertions.assertThat; - public abstract class BaseTestCase { protected static Logger logger = LoggerFactory.getLogger(BaseTestCase.class); diff --git a/ebean-ddl-generator/src/test/java/io/localtest/DbMigrationSqlServerTest.java b/ebean-ddl-generator/src/test/java/io/localtest/DbMigrationSqlServerTest.java index 3e63179ec..fbe23d7b3 100644 --- a/ebean-ddl-generator/src/test/java/io/localtest/DbMigrationSqlServerTest.java +++ b/ebean-ddl-generator/src/test/java/io/localtest/DbMigrationSqlServerTest.java @@ -3,7 +3,6 @@ package io.localtest; import io.ebean.annotation.Platform; import io.ebean.dbmigration.DbMigration; import io.ebeaninternal.api.DbOffline; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertThrows; diff --git a/ebean-ddl-generator/src/test/resources/assert/BaseDdlHandlerTest/baseApply.sql b/ebean-ddl-generator/src/test/resources/assert/BaseDdlHandlerTest/baseApply.sql index 74f140511..46f430bce 100644 --- a/ebean-ddl-generator/src/test/resources/assert/BaseDdlHandlerTest/baseApply.sql +++ b/ebean-ddl-generator/src/test/resources/assert/BaseDdlHandlerTest/baseApply.sql @@ -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; - diff --git a/ebean-ddl-generator/src/test/resources/assert/BaseDdlHandlerTest/baseDropAll.sql b/ebean-ddl-generator/src/test/resources/assert/BaseDdlHandlerTest/baseDropAll.sql index cf3ebf022..170706608 100644 --- a/ebean-ddl-generator/src/test/resources/assert/BaseDdlHandlerTest/baseDropAll.sql +++ b/ebean-ddl-generator/src/test/resources/assert/BaseDdlHandlerTest/baseDropAll.sql @@ -1,2 +1,3 @@ +-- drop all drop table if exists foo; diff --git a/ebean-ddl-generator/src/test/resources/assert/ModelBuild_compoundKeyTest/apply.sql b/ebean-ddl-generator/src/test/resources/assert/ModelBuild_compoundKeyTest/apply.sql index 44283c3bf..96ded6d9b 100644 --- a/ebean-ddl-generator/src/test/resources/assert/ModelBuild_compoundKeyTest/apply.sql +++ b/ebean-ddl-generator/src/test/resources/assert/ModelBuild_compoundKeyTest/apply.sql @@ -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; diff --git a/ebean-ddl-generator/src/test/resources/assert/ModelBuild_explicitSequencesTest/apply.sql b/ebean-ddl-generator/src/test/resources/assert/ModelBuild_explicitSequencesTest/apply.sql index c84e3c252..bc0f0635f 100644 --- a/ebean-ddl-generator/src/test/resources/assert/ModelBuild_explicitSequencesTest/apply.sql +++ b/ebean-ddl-generator/src/test/resources/assert/ModelBuild_explicitSequencesTest/apply.sql @@ -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; diff --git a/ebean-ddl-generator/src/test/resources/assert/ModelBuild_explicitSequencesTest/pg-apply.sql b/ebean-ddl-generator/src/test/resources/assert/ModelBuild_explicitSequencesTest/pg-apply.sql index e866a1f4b..e0063a84e 100644 --- a/ebean-ddl-generator/src/test/resources/assert/ModelBuild_explicitSequencesTest/pg-apply.sql +++ b/ebean-ddl-generator/src/test/resources/assert/ModelBuild_explicitSequencesTest/pg-apply.sql @@ -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; diff --git a/ebean-ddl-generator/src/test/resources/assert/create-column-table.txt b/ebean-ddl-generator/src/test/resources/assert/drop-create-column-table.txt similarity index 84% rename from ebean-ddl-generator/src/test/resources/assert/create-column-table.txt rename to ebean-ddl-generator/src/test/resources/assert/drop-create-column-table.txt index 879972680..b5b138663 100644 --- a/ebean-ddl-generator/src/test/resources/assert/create-column-table.txt +++ b/ebean-ddl-generator/src/test/resources/assert/drop-create-column-table.txt @@ -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, diff --git a/ebean-ddl-generator/src/test/resources/assert/create-table.txt b/ebean-ddl-generator/src/test/resources/assert/drop-create-table.txt similarity index 83% rename from ebean-ddl-generator/src/test/resources/assert/create-table.txt rename to ebean-ddl-generator/src/test/resources/assert/drop-create-table.txt index 3330f90e5..1b9b2ab87 100644 --- a/ebean-ddl-generator/src/test/resources/assert/create-table.txt +++ b/ebean-ddl-generator/src/test/resources/assert/drop-create-table.txt @@ -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, diff --git a/ebean-externalmapping-xml/pom.xml b/ebean-externalmapping-xml/pom.xml index 66a48c9a0..916b13166 100644 --- a/ebean-externalmapping-xml/pom.xml +++ b/ebean-externalmapping-xml/pom.xml @@ -73,7 +73,7 @@ io.avaje junit - 1.0 + 1.1 test diff --git a/ebean-kotlin/pom.xml b/ebean-kotlin/pom.xml index 1b50fe698..df37c439c 100644 --- a/ebean-kotlin/pom.xml +++ b/ebean-kotlin/pom.xml @@ -42,7 +42,7 @@ io.avaje junit - 1.0 + 1.1 test diff --git a/ebean-postgis/pom.xml b/ebean-postgis/pom.xml index 0a49ac36b..b41011ab4 100644 --- a/ebean-postgis/pom.xml +++ b/ebean-postgis/pom.xml @@ -14,7 +14,7 @@ ebean-postgis - 2.2.1 + 2.5.1 @@ -37,14 +37,14 @@ net.postgis postgis-jdbc - [${postgis.jdbc.version}] + ${postgis.jdbc.version} org.postgresql postgresql - 42.3.2 + 42.3.3 provided @@ -64,13 +64,6 @@ provided - - io.ebean - ebean-datasource - ${ebean-datasource.version} - test - - io.ebean ebean-test @@ -88,7 +81,7 @@ io.avaje junit - 1.0 + 1.1 test diff --git a/ebean-postgis/src/main/java/io/ebean/postgis/ScalarTypePgisBase.java b/ebean-postgis/src/main/java/io/ebean/postgis/ScalarTypePgisBase.java index 2b8463d0b..27bf5200b 100644 --- a/ebean-postgis/src/main/java/io/ebean/postgis/ScalarTypePgisBase.java +++ b/ebean-postgis/src/main/java/io/ebean/postgis/ScalarTypePgisBase.java @@ -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 implements ScalarType { 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); diff --git a/ebean-querybean/pom.xml b/ebean-querybean/pom.xml index 78d295e58..92b67e012 100644 --- a/ebean-querybean/pom.xml +++ b/ebean-querybean/pom.xml @@ -77,7 +77,7 @@ io.avaje junit - 1.0 + 1.1 test diff --git a/ebean-redis/pom.xml b/ebean-redis/pom.xml index 6c46d37ae..c887a3cd6 100644 --- a/ebean-redis/pom.xml +++ b/ebean-redis/pom.xml @@ -57,7 +57,7 @@ io.avaje junit - 1.0 + 1.1 test diff --git a/ebean-test/pom.xml b/ebean-test/pom.xml index a6fae7b14..e5ad64b5c 100644 --- a/ebean-test/pom.xml +++ b/ebean-test/pom.xml @@ -147,7 +147,7 @@ org.postgresql postgresql - 42.3.2 + 42.3.3 org.checkerframework @@ -233,7 +233,7 @@ io.avaje junit - 1.0 + 1.1 test diff --git a/ebean-test/src/main/java/io/ebean/test/config/AutoConfigureForTesting.java b/ebean-test/src/main/java/io/ebean/test/config/AutoConfigureForTesting.java index 5366597a1..afd83b4d0 100644 --- a/ebean-test/src/main/java/io/ebean/test/config/AutoConfigureForTesting.java +++ b/ebean-test/src/main/java/io/ebean/test/config/AutoConfigureForTesting.java @@ -10,8 +10,6 @@ import org.slf4j.LoggerFactory; import java.util.Properties; -import javax.sql.DataSource; - /** * Automatically configure ServerConfig for testing purposes. *

diff --git a/ebean-test/src/main/java/io/ebean/test/config/platform/Config.java b/ebean-test/src/main/java/io/ebean/test/config/platform/Config.java index 117c04a18..8bde15468 100644 --- a/ebean-test/src/main/java/io/ebean/test/config/platform/Config.java +++ b/ebean-test/src/main/java/io/ebean/test/config/platform/Config.java @@ -5,7 +5,6 @@ import io.ebean.datasource.DataSourceConfig; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; -import java.util.Locale; import java.util.Properties; /** diff --git a/ebean-test/src/test/java/io/ebean/BaseTestCase.java b/ebean-test/src/test/java/io/ebean/BaseTestCase.java index c44ce2092..43ba79427 100644 --- a/ebean-test/src/test/java/io/ebean/BaseTestCase.java +++ b/ebean-test/src/test/java/io/ebean/BaseTestCase.java @@ -195,6 +195,14 @@ public abstract class BaseTestCase { return Platform.DB2 == platform(); } + public boolean isSqLite() { + return Platform.SQLITE == platform(); + } + + public boolean isClickHouse() { + return Platform.CLICKHOUSE == platform(); + } + public boolean platformDistinctOn() { return isPostgresCompatible(); } diff --git a/ebean-test/src/test/java/io/ebean/EbeanServer_refresh.java b/ebean-test/src/test/java/io/ebean/EbeanServer_refresh.java index 1ff650b7d..2d1e957e7 100644 --- a/ebean-test/src/test/java/io/ebean/EbeanServer_refresh.java +++ b/ebean-test/src/test/java/io/ebean/EbeanServer_refresh.java @@ -9,7 +9,6 @@ import org.tests.model.basic.ResetBasicData; import java.io.File; import java.io.IOException; import java.nio.file.Files; -import java.nio.file.OpenOption; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.util.*; diff --git a/ebean-test/src/test/java/io/ebeaninternal/dbmigration/DbMigrationDropHistoryTest.java b/ebean-test/src/test/java/io/ebeaninternal/dbmigration/DbMigrationDropHistoryTest.java index 6133928b4..3f7702884 100644 --- a/ebean-test/src/test/java/io/ebeaninternal/dbmigration/DbMigrationDropHistoryTest.java +++ b/ebean-test/src/test/java/io/ebeaninternal/dbmigration/DbMigrationDropHistoryTest.java @@ -11,6 +11,7 @@ import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; import java.util.Arrays; import java.util.List; @@ -32,10 +33,18 @@ public class DbMigrationDropHistoryTest { main(null); } + @Test + public void lastVersion_no_v_Prefix() { + File d = new File("src/test/resources/migrationtest-history/dbmigration"); + assertThat(LastMigration.lastVersion(d, null)).isEqualTo("1.2"); + } + public static void main(String[] args) throws IOException { logger.info("start"); - + // First, we clean up the output-directory + Files.walk(Paths.get("src/test/resources/migrationtest-history")) + .filter(Files::isRegularFile).map(Path::toFile).forEach(File::delete); DefaultDbMigration migration = new DefaultDbMigration(); // We use src/test/resources as output directory (so we see in GIT if files will change) diff --git a/ebean-test/src/test/java/io/ebeaninternal/dbmigration/DbMigrationGenerateTest.java b/ebean-test/src/test/java/io/ebeaninternal/dbmigration/DbMigrationGenerateTest.java index 68204b602..d8b6a729e 100644 --- a/ebean-test/src/test/java/io/ebeaninternal/dbmigration/DbMigrationGenerateTest.java +++ b/ebean-test/src/test/java/io/ebeaninternal/dbmigration/DbMigrationGenerateTest.java @@ -1,7 +1,8 @@ package io.ebeaninternal.dbmigration; - +import io.ebean.Database; import io.ebean.DatabaseFactory; +import io.ebean.annotation.Platform; import io.ebean.config.DatabaseConfig; import org.junit.jupiter.api.Test; import org.slf4j.Logger; @@ -15,10 +16,20 @@ import java.nio.file.Paths; import java.util.Arrays; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; /** - * This is the Migrationscript generator. It generates 3 migrationscript for the models + * This is the Migrationscript generator. It will generate migrationscripts for all platforms. + * It uses the packages 'misc.migration' + *

    + *
  • misc.migration.v1_0 Initial package. + *
  • misc.migration.v1_1 modified v1_0 - every migration change, that ebean supports should be done here. + *
  • misc.migration.v1_2 This is (nearly) the same as v1_0 and simulates every migration change in the other way + *
+ * + * Changes in the migration scripts should be committed after they are reviewed (changes may be legitime or show, that something is broken). + * The {@link DbMigrationTest} will execute the generated scripts for various platforms. * @author Roland Praml, FOCONIS AG * */ @@ -38,46 +49,99 @@ public class DbMigrationGenerateTest { public static void run(String pathToResources) throws IOException { logger.info("start current directory: " + new File(".").getAbsolutePath()); + + // First, we clean up the output-directory + Files.walk(Paths.get(pathToResources, "migrationtest")) + .filter(Files::isRegularFile).map(Path::toFile).forEach(File::delete); + + DefaultDbMigration migration = new DefaultDbMigration(); + migration.setIncludeIndex(true); + // We use src/test/resources as output directory (so we see in GIT if files will change) + migration.setPathToResources(pathToResources); + + migration.addPlatform(Platform.CLICKHOUSE); + migration.addPlatform(Platform.COCKROACH); + // for platforms like DB2-LUW, we specify the exact platform in the migration path + // see https://github.com/ebean-orm/ebean-migration/issues/102 + migration.addPlatform(Platform.DB2LUW, "db2luw"); + migration.addPlatform(Platform.DB2FORI, "db2fori"); + migration.addPlatform(Platform.DB2ZOS, "db2zos"); + migration.addPlatform(Platform.DB2, "db2legacy"); + migration.addPlatform(Platform.GENERIC); + migration.addPlatform(Platform.H2); + migration.addPlatform(Platform.HANA); + migration.addPlatform(Platform.HSQLDB); + migration.addPlatform(Platform.MARIADB); + migration.addPlatform(Platform.MARIADB, "mariadb-noprocs"); + migration.addPlatform(Platform.MYSQL); + migration.addPlatform(Platform.MYSQL55, "mysql55"); + migration.addPlatform(Platform.NUODB); + migration.addPlatform(Platform.ORACLE); + migration.addPlatform(Platform.ORACLE11, "oracle11"); // uses sequences + migration.addPlatform(Platform.POSTGRES); + migration.addPlatform(Platform.POSTGRES9, "postgres9"); // different DDL as base! + migration.addPlatform(Platform.SQLANYWHERE); + migration.addPlatform(Platform.SQLITE); + migration.addPlatform(Platform.SQLSERVER16, "sqlserver16"); + migration.addPlatform(Platform.SQLSERVER17, "sqlserver17"); + migration.addPlatform(Platform.YUGABYTE); + + DatabaseConfig config = new DatabaseConfig(); config.setName("migrationtest"); config.loadFromProperties(); config.setRegister(false); config.setDefaultServer(false); - config.getProperties().put("ebean.migrationtest.migration.pathToResources", pathToResources); - config.setPackages(Arrays.asList("misc.migration.v1_0")); + config.getProperties().put("ebean.hana.generateUniqueDdl", "true"); // need to generate unique statements to prevent them from being filtered out as duplicates by the DdlRunner - // First, we clean up the output-directory - Files.walk(Paths.get(pathToResources, "migrationtest")) - .filter(Files::isRegularFile).map(Path::toFile).forEach(File::delete); - - DatabaseFactory.create(config).shutdown(); + config.setPackages(Arrays.asList("misc.migration.v1_0")); + Database server = DatabaseFactory.create(config); + migration.setServer(server); // then we generate migration scripts for v1_0 - assertThat(DbMigrationPlugin.getLastMigration()).isEqualTo("1.0__initial"); - assertThat(DbMigrationPlugin.getLastInit()).isEqualTo("1.0__initial"); - + assertThat(migration.generateMigration()).isEqualTo("1.0__initial"); // and we check repeatative calls - DatabaseFactory.create(config).shutdown(); - assertThat(DbMigrationPlugin.getLastMigration()).isNull(); - assertThat(DbMigrationPlugin.getLastInit()).isNull(); - + assertThat(migration.generateMigration()).isNull(); + // and now for v1_1 config.setPackages(Arrays.asList("misc.migration.v1_1")); - DatabaseFactory.create(config).shutdown(); - assertThat(DbMigrationPlugin.getLastMigration()).isEqualTo("1.1,1.2__dropsFor_1.1"); - assertThat(DbMigrationPlugin.getLastInit()).isEqualTo("1.2"); + server.shutdown(); + server = DatabaseFactory.create(config); + migration.setServer(server); + assertThat(migration.generateMigration()).isEqualTo("1.1"); + assertThat(migration.generateMigration()).isNull(); // subsequent call - // subsequent call - DatabaseFactory.create(config).shutdown(); - assertThat(DbMigrationPlugin.getLastMigration()).isNull(); - assertThat(DbMigrationPlugin.getLastInit()).isNull(); - - // and now for v1_2 + + + System.setProperty("ddl.migration.pendingDropsFor", "1.1"); + assertThat(migration.generateMigration()).isEqualTo("1.2__dropsFor_1.1"); + + assertThatThrownBy(()->migration.generateMigration()) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("No 'pendingDrops'"); // subsequent call + + System.clearProperty("ddl.migration.pendingDropsFor"); + assertThat(migration.generateMigration()).isNull(); // subsequent call + + // and now for v1_2 with config.setPackages(Arrays.asList("misc.migration.v1_2")); - DatabaseFactory.create(config).shutdown(); - assertThat(DbMigrationPlugin.getLastMigration()).isEqualTo("1.3,1.4__dropsFor_1.3"); - assertThat(DbMigrationPlugin.getLastInit()).isEqualTo("1.4"); + server.shutdown(); + server = DatabaseFactory.create(config); + migration.setServer(server); + assertThat(migration.generateMigration()).isEqualTo("1.3"); + assertThat(migration.generateMigration()).isNull(); // subsequent call + + System.setProperty("ddl.migration.pendingDropsFor", "1.3"); + assertThat(migration.generateMigration()).isEqualTo("1.4__dropsFor_1.3"); + assertThatThrownBy(migration::generateMigration) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("No 'pendingDrops'"); // subsequent call + + System.clearProperty("ddl.migration.pendingDropsFor"); + assertThat(migration.generateMigration()).isNull(); // subsequent call + + server.shutdown(); logger.info("end"); } diff --git a/ebean-test/src/test/java/io/ebeaninternal/server/profile/BasicProfileLocationTest.java b/ebean-test/src/test/java/io/ebeaninternal/server/profile/BasicProfileLocationTest.java index 3c3055151..d08afe225 100644 --- a/ebean-test/src/test/java/io/ebeaninternal/server/profile/BasicProfileLocationTest.java +++ b/ebean-test/src/test/java/io/ebeaninternal/server/profile/BasicProfileLocationTest.java @@ -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"); diff --git a/ebean-test/src/test/java/io/ebeaninternal/server/type/DatesAndTimesTest.java b/ebean-test/src/test/java/io/ebeaninternal/server/type/DatesAndTimesTest.java index 50d466e3a..7929e5d1a 100644 --- a/ebean-test/src/test/java/io/ebeaninternal/server/type/DatesAndTimesTest.java +++ b/ebean-test/src/test/java/io/ebeaninternal/server/type/DatesAndTimesTest.java @@ -1,7 +1,6 @@ package io.ebeaninternal.server.type; import static java.lang.String.format; -import static org.junit.jupiter.api.Assumptions.assumeFalse; import java.sql.Date; import java.sql.Timestamp; @@ -18,13 +17,11 @@ import java.util.Calendar; import java.util.List; import java.util.TimeZone; -import javax.sql.DataSource; import org.assertj.core.api.SoftAssertions; import org.joda.time.DateTimeZone; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; diff --git a/ebean-test/src/test/java/io/ebeaninternal/server/type/TestTypeManager.java b/ebean-test/src/test/java/io/ebeaninternal/server/type/TestTypeManager.java index 383671363..94aa02e3d 100644 --- a/ebean-test/src/test/java/io/ebeaninternal/server/type/TestTypeManager.java +++ b/ebean-test/src/test/java/io/ebeaninternal/server/type/TestTypeManager.java @@ -15,7 +15,6 @@ import org.tests.model.ivo.converter.MoneyTypeConverter; import javax.persistence.EnumType; import java.sql.SQLException; import java.sql.Types; -import java.util.Calendar; import java.util.GregorianCalendar; import static org.assertj.core.api.Assertions.assertThat; diff --git a/ebean-test/src/test/java/misc/migration/v1_0/EBasic.java b/ebean-test/src/test/java/misc/migration/v1_0/EBasic.java index c579161eb..828c18da2 100644 --- a/ebean-test/src/test/java/misc/migration/v1_0/EBasic.java +++ b/ebean-test/src/test/java/misc/migration/v1_0/EBasic.java @@ -47,7 +47,7 @@ public class EBasic { @Size(max=127) String description; - + @Lob @Column(columnDefinition = "db2;blob(64M);") File descriptionFile; diff --git a/ebean-test/src/test/java/misc/migration/v1_1/EHistory.java b/ebean-test/src/test/java/misc/migration/v1_1/EHistory.java index d949d4838..f67b4c9f2 100644 --- a/ebean-test/src/test/java/misc/migration/v1_1/EHistory.java +++ b/ebean-test/src/test/java/misc/migration/v1_1/EHistory.java @@ -23,4 +23,20 @@ public class EHistory { @DbMigration(platforms = Platform.POSTGRES, preAlter = "alter table ${table} alter column ${column} TYPE bigint USING (${column}::integer)") Long testString; + + public Long getTestString() { + return testString; + } + + public void setTestString(Long testString) { + this.testString = testString; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } } diff --git a/ebean-test/src/test/java/misc/migration/v1_1/EHistory2.java b/ebean-test/src/test/java/misc/migration/v1_1/EHistory2.java index 750ad4e62..0c58ab84b 100644 --- a/ebean-test/src/test/java/misc/migration/v1_1/EHistory2.java +++ b/ebean-test/src/test/java/misc/migration/v1_1/EHistory2.java @@ -41,4 +41,45 @@ public class EHistory2 { @Size(max = 20) String newColumn; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getTestString() { + return testString; + } + + public void setTestString(String testString) { + this.testString = testString; + } + + public String getTestString2() { + return testString2; + } + + public void setTestString2(String testString2) { + this.testString2 = testString2; + } + + public String getTestString3() { + return testString3; + } + + public void setTestString3(String testString3) { + this.testString3 = testString3; + } + + public String getNewColumn() { + return newColumn; + } + + public void setNewColumn(String newColumn) { + this.newColumn = newColumn; + } + } diff --git a/ebean-test/src/test/java/org/tests/basic/lob/TestBasicClobNoVer.java b/ebean-test/src/test/java/org/tests/basic/lob/TestBasicClobNoVer.java index 7289a7ac2..274912594 100644 --- a/ebean-test/src/test/java/org/tests/basic/lob/TestBasicClobNoVer.java +++ b/ebean-test/src/test/java/org/tests/basic/lob/TestBasicClobNoVer.java @@ -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 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(); + } } diff --git a/ebean-test/src/test/java/org/tests/inheritance/bothsides/Target1.java b/ebean-test/src/test/java/org/tests/inheritance/bothsides/Target1.java index f39c3ee61..0febee7cf 100644 --- a/ebean-test/src/test/java/org/tests/inheritance/bothsides/Target1.java +++ b/ebean-test/src/test/java/org/tests/inheritance/bothsides/Target1.java @@ -1,7 +1,6 @@ package org.tests.inheritance.bothsides; import javax.persistence.Entity; -import javax.persistence.Inheritance; @Entity public class Target1 extends TargetBase { diff --git a/ebean-test/src/test/java/org/tests/model/basic/EBasicClobNoVer.java b/ebean-test/src/test/java/org/tests/model/basic/EBasicClobNoVer.java index 735ad796e..f946eb82b 100644 --- a/ebean-test/src/test/java/org/tests/model/basic/EBasicClobNoVer.java +++ b/ebean-test/src/test/java/org/tests/model/basic/EBasicClobNoVer.java @@ -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 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 children() { + return children; + } + + public EBasicClobNoVer children(List children) { + this.children = children; + return this; + } } diff --git a/ebean-test/src/test/java/org/tests/model/basic/EBasicClobNoVerChild.java b/ebean-test/src/test/java/org/tests/model/basic/EBasicClobNoVerChild.java new file mode 100644 index 000000000..8af997f69 --- /dev/null +++ b/ebean-test/src/test/java/org/tests/model/basic/EBasicClobNoVerChild.java @@ -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; + } +} diff --git a/ebean-test/src/test/java/org/tests/model/basic/MDateTime.java b/ebean-test/src/test/java/org/tests/model/basic/MDateTime.java index 15aa3cec5..010d37860 100644 --- a/ebean-test/src/test/java/org/tests/model/basic/MDateTime.java +++ b/ebean-test/src/test/java/org/tests/model/basic/MDateTime.java @@ -16,8 +16,6 @@ import javax.annotation.Nullable; import javax.persistence.Entity; import javax.persistence.Id; -import org.joda.time.DateTime; - @Entity public class MDateTime { diff --git a/ebean-test/src/test/java/org/tests/model/basic/TWithPreInsert.java b/ebean-test/src/test/java/org/tests/model/basic/TWithPreInsert.java index acfffce20..a19c89c7d 100644 --- a/ebean-test/src/test/java/org/tests/model/basic/TWithPreInsert.java +++ b/ebean-test/src/test/java/org/tests/model/basic/TWithPreInsert.java @@ -28,6 +28,7 @@ public class TWithPreInsert implements TWithPreInsertCommon { */ transient int requestCascadeState; + @Override public Integer getId() { return id; } @@ -36,18 +37,22 @@ public class TWithPreInsert implements TWithPreInsertCommon { this.id = id; } + @Override public String getName() { return name; } + @Override public void setName(String name) { this.name = name; } + @Override public String getTitle() { return title; } + @Override public void setTitle(String title) { this.title = title; } @@ -60,6 +65,7 @@ public class TWithPreInsert implements TWithPreInsertCommon { return requestCascadeState; } + @Override public void requestCascadeState(int requestCascadeState) { this.requestCascadeState = requestCascadeState; } diff --git a/ebean-test/src/test/java/org/tests/model/basic/TWithPreInsertChild.java b/ebean-test/src/test/java/org/tests/model/basic/TWithPreInsertChild.java index 39a1d8dff..5f679f9f6 100644 --- a/ebean-test/src/test/java/org/tests/model/basic/TWithPreInsertChild.java +++ b/ebean-test/src/test/java/org/tests/model/basic/TWithPreInsertChild.java @@ -24,6 +24,7 @@ public class TWithPreInsertChild implements TWithPreInsertCommon { this.name = name; } + @Override public Integer getId() { return id; } @@ -32,18 +33,22 @@ public class TWithPreInsertChild implements TWithPreInsertCommon { this.id = id; } + @Override public String getName() { return name; } + @Override public void setName(String name) { this.name = name; } + @Override public String getTitle() { return title; } + @Override public void setTitle(String title) { this.title = title; } @@ -52,6 +57,7 @@ public class TWithPreInsertChild implements TWithPreInsertCommon { return requestCascadeState; } + @Override public void requestCascadeState(int requestCascadeState) { this.requestCascadeState = requestCascadeState; } diff --git a/ebean-test/src/test/java/org/tests/model/history/TestHistoryExclude.java b/ebean-test/src/test/java/org/tests/model/history/TestHistoryExclude.java index 512469c57..64a5c54a8 100644 --- a/ebean-test/src/test/java/org/tests/model/history/TestHistoryExclude.java +++ b/ebean-test/src/test/java/org/tests/model/history/TestHistoryExclude.java @@ -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() { diff --git a/ebean-test/src/test/java/org/tests/model/history/TestHistoryInclude.java b/ebean-test/src/test/java/org/tests/model/history/TestHistoryInclude.java index d59579f03..f1122447b 100644 --- a/ebean-test/src/test/java/org/tests/model/history/TestHistoryInclude.java +++ b/ebean-test/src/test/java/org/tests/model/history/TestHistoryInclude.java @@ -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() { diff --git a/ebean-test/src/test/java/org/tests/model/history/TestHistoryOneToMany.java b/ebean-test/src/test/java/org/tests/model/history/TestHistoryOneToMany.java index 07254f3b6..b5c0c43d4 100644 --- a/ebean-test/src/test/java/org/tests/model/history/TestHistoryOneToMany.java +++ b/ebean-test/src/test/java/org/tests/model/history/TestHistoryOneToMany.java @@ -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 { diff --git a/ebean-test/src/test/java/org/tests/model/lazywithcache/TestWithCacheAndLazyLoad.java b/ebean-test/src/test/java/org/tests/model/lazywithcache/TestWithCacheAndLazyLoad.java index 8c8066dbc..39ac73ded 100644 --- a/ebean-test/src/test/java/org/tests/model/lazywithcache/TestWithCacheAndLazyLoad.java +++ b/ebean-test/src/test/java/org/tests/model/lazywithcache/TestWithCacheAndLazyLoad.java @@ -3,24 +3,22 @@ package org.tests.model.lazywithcache; import io.ebean.BaseTestCase; import io.ebean.DB; import io.ebean.Transaction; - import org.junit.jupiter.api.Test; +import java.util.List; + import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertNotNull; -import java.util.List; - /** * Test with bean cache and lazy loaded property. * * @author Noemi Szemenyei, FOCONIS AG - * */ -public class TestWithCacheAndLazyLoad extends BaseTestCase{ +class TestWithCacheAndLazyLoad extends BaseTestCase { @Test - public void testGetters() { + void testGetters() { ChildWithCache child = new ChildWithCache(); child.setId(1L); @@ -52,19 +50,19 @@ public class TestWithCacheAndLazyLoad extends BaseTestCase{ assertThat(temp.getAddress()).isEqualTo("Address"); assertThat(temp.getName()).isEqualTo("Child With Cache"); } - + @Test - public void testBatch() throws Exception { - + void testBatch() { + for (int i = 0; i < 2; i++) { ChildWithCache child = new ChildWithCache(); - child.setId(1000L+i); - child.setName("Child With Cache "+ i); + child.setId(1000L + i); + child.setName("Child With Cache " + i); child.setAddress("Address"); DB.save(child); ParentA parentA = new ParentA(); - parentA.setId(1000L+i); + parentA.setId(1000L + i); parentA.setName("Parent A"); parentA.setChild(child); DB.save(parentA); @@ -72,7 +70,7 @@ public class TestWithCacheAndLazyLoad extends BaseTestCase{ // 0. load cache ParentA temp = DB.find(ParentA.class, 1000L); temp.getChild().getName(); - + try (Transaction txn = DB.beginTransaction()) { // 1. FindList List list = DB.find(ParentA.class).where().ge("id", 1000L).orderBy("id").findList(); diff --git a/ebean-test/src/test/java/org/tests/model/nofk/TestNoFk.java b/ebean-test/src/test/java/org/tests/model/nofk/TestNoFk.java index e27df52db..a13b0cdff 100644 --- a/ebean-test/src/test/java/org/tests/model/nofk/TestNoFk.java +++ b/ebean-test/src/test/java/org/tests/model/nofk/TestNoFk.java @@ -5,6 +5,7 @@ import io.ebean.DB; import io.ebean.SqlRow; import io.ebean.bean.EntityBean; import io.ebean.bean.EntityBeanIntercept; +import io.ebean.test.LoggedSql; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @@ -142,13 +143,16 @@ public class TestNoFk extends BaseTestCase { assertTrue(ownerEbi.isReference()); assertTrue(ownerEbi.isPartial()); assertTrue(ownerEbi.isLazyLoadFailure()); - } @Test public void testEagerLoadFile() { + LoggedSql.start(); List files = DB.find(EFileNoFk.class).fetch("owner").findList(); assertThat(files).hasSize(2); + List sql = LoggedSql.stop(); + assertThat(sql).hasSize(1); + assertThat(sql.get(0)).contains("select t0.file_name, t0.owner_user_id, t0.owner_soft_del_user_id, t1.user_id, t1.user_name from efile_no_fk t0 left join euser_no_fk t1 on t1.user_id = t0.owner_user_id"); EFileNoFk file1 = files.get(0); EFileNoFk file2 = files.get(1); @@ -175,7 +179,6 @@ public class TestNoFk extends BaseTestCase { assertTrue(ownerEbi.isReference()); assertTrue(ownerEbi.isPartial()); assertTrue(ownerEbi.isLazyLoadFailure()); - } @Test @@ -259,8 +262,14 @@ public class TestNoFk extends BaseTestCase { @Test public void testEagerLoadFileSoftDel() { + LoggedSql.start(); List files = DB.find(EFileNoFk.class).fetch("ownerSoftDel").findList(); assertThat(files).hasSize(2); + List sql = LoggedSql.stop(); + assertThat(sql).hasSize(1); + if (isH2() || isPostgres()) { + assertThat(sql.get(0)).contains("select t0.file_name, t0.owner_user_id, t0.owner_soft_del_user_id, t1.user_id, t1.user_name, t1.user_id is null from efile_no_fk t0 left join euser_no_fk_soft_del t1 on t1.user_id = t0.owner_soft_del_user_id"); + } EFileNoFk file1 = files.get(0); EFileNoFk file2 = files.get(1); diff --git a/ebean-test/src/test/java/org/tests/query/TestQueryMultiJoinFetchPath.java b/ebean-test/src/test/java/org/tests/query/TestQueryMultiJoinFetchPath.java index e8f1a81be..4f439a58e 100644 --- a/ebean-test/src/test/java/org/tests/query/TestQueryMultiJoinFetchPath.java +++ b/ebean-test/src/test/java/org/tests/query/TestQueryMultiJoinFetchPath.java @@ -14,7 +14,6 @@ class TestQueryMultiJoinFetchPath extends BaseTestCase { @Test void test() { - HCustomer c1 = new HCustomer("c1", "c1"); DB.save(c1); @@ -60,9 +59,9 @@ class TestQueryMultiJoinFetchPath extends BaseTestCase { assertThat(accesses).hasSize(2); if (isH2()) { - assertThat(query.getGeneratedSql()).isEqualTo("select t0.dtype, t0.id, t0.accessor_id, t0.principal_id, t2.dtype, t0.access_account_number, t0.accessor_id, t2.dtype, t0.access_account_number, t1.cid, t1.name, t2.dtype, t2.account_number from haccess t0 left join hcustomer t1 on t1.cid = t0.accessor_id left join haccount t2 on t2.account_number = t0.access_account_number and t2.dtype = 'B' left join hcustomer t3 on t3.cid = t0.principal_id where t1.status = ? and t3.status = ? and t0.id in (?,?,?,?,?)"); + assertThat(query.getGeneratedSql()).isEqualTo("select t0.dtype, t0.id, t0.accessor_id, t0.principal_id, t2.dtype, t0.access_account_number, t1.cid, t1.name, t2.dtype, t2.account_number from haccess t0 left join hcustomer t1 on t1.cid = t0.accessor_id left join haccount t2 on t2.account_number = t0.access_account_number and t2.dtype = 'B' left join hcustomer t3 on t3.cid = t0.principal_id where t1.status = ? and t3.status = ? and t0.id in (?,?,?,?,?)"); } else { - assertThat(query.getGeneratedSql()).contains("select t0.dtype, t0.id, t0.accessor_id, t0.principal_id, t2.dtype, t0.access_account_number, t0.accessor_id, t2.dtype, t0.access_account_number, t1.cid, t1.name, t2.dtype, t2.account_number from haccess t0 left join hcustomer t1 on t1.cid = t0.accessor_id left join haccount t2 on t2.account_number = t0.access_account_number and t2.dtype = 'B' left join hcustomer t3 on t3.cid = t0.principal_id where t1.status = ? and t3.status = ? and t0.id "); + assertThat(query.getGeneratedSql()).contains("select t0.dtype, t0.id, t0.accessor_id, t0.principal_id, t2.dtype, t0.access_account_number, t1.cid, t1.name, t2.dtype, t2.account_number from haccess t0 left join hcustomer t1 on t1.cid = t0.accessor_id left join haccount t2 on t2.account_number = t0.access_account_number and t2.dtype = 'B' left join hcustomer t3 on t3.cid = t0.principal_id where t1.status = ? and t3.status = ? and t0.id "); } } diff --git a/ebean-test/src/test/java/org/tests/query/finder/TestCustomerFinder.java b/ebean-test/src/test/java/org/tests/query/finder/TestCustomerFinder.java index a69fc0f84..7903adb9c 100644 --- a/ebean-test/src/test/java/org/tests/query/finder/TestCustomerFinder.java +++ b/ebean-test/src/test/java/org/tests/query/finder/TestCustomerFinder.java @@ -3,6 +3,8 @@ package org.tests.query.finder; import io.ebean.BaseTestCase; import io.ebean.DB; import io.ebean.Transaction; +import io.ebean.annotation.IgnorePlatform; +import io.ebean.annotation.Platform; import io.ebean.meta.*; import io.ebean.test.LoggedSql; import org.junit.jupiter.api.Test; @@ -163,6 +165,7 @@ public class TestCustomerFinder extends BaseTestCase { } @Test + @IgnorePlatform(Platform.DB2) // no query plans yet, for DB2 public void test_finders_queryPlans() { ResetBasicData.reset(); diff --git a/ebean-test/src/test/java/org/tests/rawsql/TestRawSqlNamedParams.java b/ebean-test/src/test/java/org/tests/rawsql/TestRawSqlNamedParams.java index 319469cf7..248f38789 100644 --- a/ebean-test/src/test/java/org/tests/rawsql/TestRawSqlNamedParams.java +++ b/ebean-test/src/test/java/org/tests/rawsql/TestRawSqlNamedParams.java @@ -13,7 +13,6 @@ import java.util.List; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.fail; public class TestRawSqlNamedParams extends BaseTestCase { diff --git a/ebean-test/src/test/java/org/tests/text/json/TestJsonSimple.java b/ebean-test/src/test/java/org/tests/text/json/TestJsonSimple.java index c462a80cc..b95c889e7 100644 --- a/ebean-test/src/test/java/org/tests/text/json/TestJsonSimple.java +++ b/ebean-test/src/test/java/org/tests/text/json/TestJsonSimple.java @@ -9,7 +9,6 @@ import io.ebean.util.IOUtils; import org.junit.jupiter.api.Test; import java.io.*; -import java.nio.charset.StandardCharsets; import java.util.LinkedHashMap; import java.util.Map; diff --git a/ebean-test/src/test/resources/ebean.properties b/ebean-test/src/test/resources/ebean.properties index 52027d197..f4059e4fe 100644 --- a/ebean-test/src/test/resources/ebean.properties +++ b/ebean-test/src/test/resources/ebean.properties @@ -149,7 +149,7 @@ datasource.yugabyte.username=unit datasource.yugabyte.password=test datasource.yugabyte.url=jdbc:postgresql://localhost:6433/unit?sslmode=disable -datasource.oracle.username=test_ebean +datasource.oracle.username=test_eb datasource.oracle.password=test datasource.oracle.url=jdbc:oracle:thin:@localhost:1521:XE #datasource.oracle.driver=oracle.jdbc.driver.OracleDriver @@ -221,15 +221,17 @@ dbmigration.platform.mariadb.useMigrationStoredProcedures=true dbmigration.platform.mysql.useMigrationStoredProcedures=true -# need to generate unique statements to prevent them from being filtered out as duplicates by the DdlRunner -ebean.hana.generateUniqueDdl=true +ebean.migrationtest.migration.strict=true +# enable stored procedures f +dbmigration.platform.mariadb.useMigrationStoredProcedures=true +dbmigration.platform.mysql.useMigrationStoredProcedures=true +dbmigration.platform.mysql55.useMigrationStoredProcedures=true # parameters for migration test datasource.migrationtest-history.username=SA datasource.migrationtest-history.password=SA datasource.migrationtest-history.url=jdbc:h2:mem:migration -ebean.migrationtest-history.applyPrefix=V ebean.migrationtest-history.ddl.generate=false ebean.migrationtest-history.ddl.run=false ebean.migrationtest-history.ddl.header=-- Migrationscripts for ebean unittest DbMigrationDropHistoryTest diff --git a/ebean-test/testconfig/ebean-cockroach.properties b/ebean-test/testconfig/ebean-cockroach.properties index 2604e7dcf..604387b76 100644 --- a/ebean-test/testconfig/ebean-cockroach.properties +++ b/ebean-test/testconfig/ebean-cockroach.properties @@ -1,4 +1,5 @@ ebean.test.platform=cockroach ebean.test.dbName=unit datasource.default=cockroach +#ebean.test.cockroach.version=v21.2.6 diff --git a/ebean-test/testconfig/ebean-hana.properties b/ebean-test/testconfig/ebean-hana.properties new file mode 100644 index 000000000..f7235d90c --- /dev/null +++ b/ebean-test/testconfig/ebean-hana.properties @@ -0,0 +1,6 @@ +# Note: You need a running docker container. See howto-test.md for details +datasource.default=hana +datasource.hana.username=SYSTEM +datasource.hana.password=VeryVerySecret#1234 +datasource.hana.url=jdbc:sap://localhost:39041/ + diff --git a/howto-test.md b/howto-test.md index 2913b24a9..63d7f900e 100644 --- a/howto-test.md +++ b/howto-test.md @@ -43,3 +43,25 @@ Maven Use the `-Dprops.file` parameter to start the test cases for a certain platform. +SAP HANA +-------- +To set up a SAP HANA container, you may need a docker account to download the image + +1. Remove any existing HANA container with `docker rm hana` + +2. Create an empty directory (e.g. `/tmp/hana`) + +3. Create a `settings.json` file with content `{ "master_password": "VeryVerySecret#1234" }` in that directory + +4. Run the container, e.g with this command: + +``` +docker run -p 39013:39013 -p 39017:39017 -p 39041-39045:39041-39045 -p 1128-1129:1128-1129 -p 59013-59014:59013-59014 \ + -v /tmp/hana:/hana/mounts \ + --ulimit nofile=1048576:1048576 --sysctl kernel.shmmax=1073741824 + --sysctl net.ipv4.ip_local_port_range="40000 60999" --sysctl kernel.shmall=8388608 \ + --name "hana" store/saplabs/hanaexpress:2.00.045.00.20200121.1 \ + --passwords-url file:///hana/mounts/settings.json --agree-to-sap-license +``` + +You can optionally add the `-d` parameter. Note, if you re-run that command, the best advice is to clear the data directory and recreate the settings.json file diff --git a/kotlin-querybean-generator/pom.xml b/kotlin-querybean-generator/pom.xml index 9739dba3f..716c2ddde 100644 --- a/kotlin-querybean-generator/pom.xml +++ b/kotlin-querybean-generator/pom.xml @@ -22,7 +22,7 @@ io.avaje junit - 1.0 + 1.1 test diff --git a/pom.xml b/pom.xml index d85a2c925..332ab0550 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.avaje java8-oss - 3.3 + 3.6 io.ebean @@ -39,15 +39,15 @@ false - 2.12.1 + 2.13.2 2.1.210 - 1.2 - 1.1 - 12.15.0 - 4.2 + 1.3 + 1.2 + 12.16.0 + 4.5 7.5 - 12.15.0 - 12.15.0 + 12.16.0 + 12.16.0 @@ -73,14 +73,14 @@ org.slf4j slf4j-api - 1.7.30 + 1.7.36 provided ch.qos.logback logback-classic - 1.2.9 + 1.2.11 test diff --git a/querybean-generator/pom.xml b/querybean-generator/pom.xml index 4a7d8296d..1b93b331a 100644 --- a/querybean-generator/pom.xml +++ b/querybean-generator/pom.xml @@ -16,7 +16,7 @@ io.avaje junit - 1.0 + 1.1 test diff --git a/tests/test-java16/pom.xml b/tests/test-java16/pom.xml index 03d9543aa..e24dc0f77 100644 --- a/tests/test-java16/pom.xml +++ b/tests/test-java16/pom.xml @@ -20,7 +20,7 @@ io.ebean ebean - 12.16.1-SNAPSHOT + 12.16.0-SNAPSHOT @@ -32,14 +32,14 @@ io.ebean ebean-test - 12.16.1-SNAPSHOT + 12.16.0-SNAPSHOT test io.avaje junit - 1.0 + 1.1 test @@ -58,7 +58,7 @@ io.ebean querybean-generator - 12.15.1-SNAPSHOT + 12.16.0-SNAPSHOT diff --git a/tests/test-kotlin/pom.xml b/tests/test-kotlin/pom.xml index 8bdeef9c7..f6832662b 100644 --- a/tests/test-kotlin/pom.xml +++ b/tests/test-kotlin/pom.xml @@ -1,11 +1,13 @@ - + 4.0.0 org.avaje java11-oss 3.4 - + test-kotlin @@ -27,21 +29,21 @@ io.avaje junit - 1.0 + 1.1 test io.ebean ebean-test - 12.16.1-SNAPSHOT + 12.16.0-SNAPSHOT test io.ebean ebean-core - 12.16.1-SNAPSHOT + 12.16.0-SNAPSHOT test @@ -107,9 +109,4 @@ - - - HEAD - scm:git:git@github.com:FOCONIS/ebean.git -