Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Roland Praml
2022-03-16 14:04:20 +01:00
147 changed files with 2859 additions and 2074 deletions
+39
View File
@@ -0,0 +1,39 @@
name: JDK EA
on:
workflow_dispatch:
schedule:
- cron: '39 6 * * 1,3,5'
jobs:
build:
runs-on: ${{ matrix.os }}
permissions:
contents: read
packages: write
strategy:
fail-fast: false
matrix:
java_version: [17,18,19,loom,valhalla,metropolis,panama]
os: [ubuntu-latest]
steps:
- uses: actions/checkout@v2
- name: Set up Java
uses: oracle-actions/setup-java@v1
with:
website: jdk.java.net
release: ${{ matrix.java_version }}
- name: Maven cache
uses: actions/cache@v2
env:
cache-name: maven-cache
with:
path:
~/.m2
key: build-${{ env.cache-name }}
- name: Build with Maven
run: mvn package
+201
View File
@@ -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.
-15
View File
@@ -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.
+1 -1
View File
@@ -106,7 +106,7 @@
<dependency>
<groupId>io.avaje</groupId>
<artifactId>junit</artifactId>
<version>1.0</version>
<version>1.1</version>
<scope>test</scope>
</dependency>
@@ -98,4 +98,14 @@ public class Version<T> {
public Map<String, ValuePair> getDiff() {
return diff;
}
@Override
public String toString() {
return "Version{" +
"bean=" + bean +
", start=" + start +
", end=" + end +
", diff=" + diff +
'}';
}
}
@@ -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);
}
@@ -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.
*
@@ -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);
@@ -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
}
}
@@ -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));
@@ -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";
}
}
@@ -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.
@@ -92,4 +92,8 @@ public class HanaPlatform extends DatabasePlatform {
}
}
@Override
public boolean isUseMigrationStoredProcedures() {
return true;
}
}
@@ -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}.
@@ -1,7 +1,6 @@
package io.ebean.plugin;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public interface DeployBeanPropertyMeta {
@@ -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;
+1 -1
View File
@@ -48,7 +48,7 @@
<dependency>
<groupId>io.avaje</groupId>
<artifactId>junit</artifactId>
<version>1.0</version>
<version>1.1</version>
<scope>test</scope>
</dependency>
+3 -3
View File
@@ -23,7 +23,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-ddl-runner</artifactId>
<version>1.2</version>
<version>${ebean-ddl-runner.version}</version>
</dependency>
<dependency>
@@ -130,7 +130,7 @@
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.3.2</version>
<version>42.3.3</version>
<optional>true</optional>
</dependency>
@@ -146,7 +146,7 @@
<dependency>
<groupId>io.avaje</groupId>
<artifactId>junit</artifactId>
<version>1.0</version>
<version>1.1</version>
<scope>test</scope>
</dependency>
@@ -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;
@@ -1,6 +1,5 @@
package io.ebeaninternal.api;
import io.ebean.meta.QueryPlanRequest;
import io.ebeaninternal.server.type.bindcapture.BindCapture;
/**
@@ -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;
@@ -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.
*/
@@ -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;
@@ -106,7 +106,9 @@ final class DScriptRunner implements ScriptRunner {
try (Connection connection = obtainConnection()) {
DdlRunner runner = new DdlRunner(useAutoCommit, scriptName, platformName);
runner.runAll(content, connection);
connection.commit();
if (!connection.getAutoCommit()) {
connection.commit();
}
runner.runNonTransactional(connection);
}
@@ -2,7 +2,6 @@ package io.ebeaninternal.server.core;
import io.ebean.CacheMode;
import io.ebean.ExpressionList;
import io.ebean.Transaction;
import io.ebean.bean.BeanCollection;
import io.ebean.bean.EntityBean;
import io.ebean.bean.EntityBeanIntercept;
@@ -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) {
@@ -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 <code>isLob()</code> or the type will effectively map to a lob.
* Returns true if this <code>isLob()</code> 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:
@@ -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;
@@ -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);
}
@@ -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;
/**
@@ -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;
@@ -13,30 +13,22 @@ import java.util.Set;
final class SqlTree {
private final SqlTreeNode rootNode;
/**
* Property if resultSet contains master and detail rows.
*/
private final STreePropertyAssocMany manyProperty;
private final String distinctOn;
private final String selectSql;
private final String fromSql;
private final String groupBy;
/**
* Encrypted Properties require additional binding.
*/
private final STreeProperty[] encryptedProps;
/**
* Where clause for inheritance.
*/
private final String inheritanceWhereSql;
private final boolean noJoins;
/**
@@ -44,7 +36,6 @@ final class SqlTree {
*/
SqlTree(SqlTreeNode rootNode, String distinctOn, String selectSql, String fromSql, String groupBy, String inheritanceWhereSql,
STreeProperty[] encryptedProps, STreePropertyAssocMany manyProperty, boolean includeJoins) {
this.rootNode = rootNode;
this.distinctOn = distinctOn;
this.selectSql = selectSql;
@@ -15,22 +15,14 @@ import java.util.TreeSet;
final class SqlTreeAlias {
private final SpiQuery.TemporalMode temporalMode;
private int counter;
private int manyWhereCounter;
private final TreeSet<String> joinProps = new TreeSet<>();
private HashSet<String> embeddedPropertyJoins;
private final TreeSet<String> manyWhereJoinProps = new TreeSet<>();
private final HashMap<String, String> aliasMap = new HashMap<>();
private final HashMap<String, String> manyWhereAliasMap = new HashMap<>();
private final String rootTableAlias;
private int counter;
private int manyWhereCounter;
SqlTreeAlias(String rootTableAlias, SpiQuery.TemporalMode temporalMode) {
this.rootTableAlias = rootTableAlias;
@@ -224,8 +224,7 @@ public final class SqlTreeBuilder {
* Recursively build the query tree depending on what leaves in the tree
* should be included.
*/
private SqlTreeNode buildSelectChain(String prefix, STreePropertyAssoc prop,
STreeType desc, List<SqlTreeNode> joinList) {
private SqlTreeNode buildSelectChain(String prefix, STreePropertyAssoc prop, STreeType desc, List<SqlTreeNode> joinList) {
List<SqlTreeNode> myJoinList = new ArrayList<>();
List<STreePropertyAssocOne> extraProps = new ArrayList<>();
for (STreePropertyAssocOne one : desc.propsOne()) {
@@ -256,7 +255,7 @@ public final class SqlTreeBuilder {
}
addManyWhereJoins(myJoinList);
}
extraProps.forEach(props::add); //addExtra
extraProps.forEach(props::addExtra);
if (!rawSql && manyWhereJoins.isFormulaWithJoin(prefix)) {
for (String property : manyWhereJoins.getFormulaJoinProperties(prefix)) {
@@ -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;
@@ -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;
@@ -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());
@@ -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;
@@ -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;
@@ -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;
/**
@@ -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();
@@ -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.
*/
@@ -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;
+2 -2
View File
@@ -16,7 +16,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-ddl-runner</artifactId>
<version>1.2</version>
<version>${ebean-ddl-runner.version}</version>
</dependency>
<dependency>
@@ -51,7 +51,7 @@
<dependency>
<groupId>io.avaje</groupId>
<artifactId>junit</artifactId>
<version>1.0</version>
<version>1.1</version>
<scope>test</scope>
</dependency>
@@ -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() {
@@ -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);
}
}
@@ -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);
}
@@ -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<Object> 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);
}
}
@@ -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);
}
@@ -0,0 +1,45 @@
package io.ebeaninternal.dbmigration.ddlgeneration;
import java.io.IOException;
/**
* Object that represents an Alter Table statements. Table alters are grouped together by tableName in DDL and can be extended by
* a ddl-specific handler (e.g. doing a reorg for DB2 or implement special grouping for Hana). There is one instance per table.
*
* @author TODO Roland Praml, FOCONIS AG
*/
public interface DdlAlterTable {
/**
* Writes the alter table statements to <code>target</code>
*/
void write(Appendable target) throws IOException;
/**
* Adds an alter table command for given column. When you invoke<br>
* <code>alterTable(writer, "my_table").add("alter column","my_column).append("type integer")</code> the resulting DDL (in
* standard implementation) would be<br>
* <code>alter table my_table alter column my_column type integer</code>
*
* @return a DdlBuffer, which can be used for further appends. Note you MUST NOT call <code>.endOfStatement()</code> on this
* buffer.
*/
DdlBuffer append(String operation, String columnName);
/**
* Adds a raw command. This is mainly used for executing user stored procedures.
*/
DdlBuffer raw(String string);
/**
* Flag that detects if history DDL (switching on) is handled for this table.
*/
boolean isHistoryHandled();
/**
* Sets the history handled flag for this table.
*/
void setHistoryHandled();
}
@@ -1,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.
@@ -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);
}
@@ -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:
* <ul>
* <li><b>applyDropDependencies:</b> Contains drops for foreign keys, indices, constraint or drop history table</li>
* <li><b>apply:</b> Contains &#64;DbMigraion.before, create table, create sequence or disable system versioning statements</li>
* <li><b>applyAlterTables:</b> Contains table alters (only that change the table data structure, there may be table alters like
* constraints etc. in postAlter)</li>
* <li><b>applyPostAlter:</b> Contains check constraints, unique constraints (which CAN be an index), column and table comments,
* &#64;DbMigraion.after, drop tables, drop sequences or enable system versioning statement</li>
* <li><b>applyForeignKeys: Contains foreign keys and indices.</b>
* <li><b>applyHistoryView:</b> The views for trigger based history support</li>
* <li><b>applyHistoryTrigger:</b> The triggers for trigger based history support</li>
* </ul>
*/
public class DdlWrite {
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<String, DdlAlterTable> 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 <code>tablename</code>.
*
* Note: All alters on a particular table are sorted in natural order in the ddl script. This allows optimizing the alters by
* merging them or doing a reorg table after altering is done.
*
* @param tableName the table name
* @param factory the factory to construct a new object
* @return
*/
public DdlAlterTable applyAlterTable(String tableName, Function<String, DdlAlterTable> factory) {
return applyAlterTables.computeIfAbsent(tableName, factory);
}
/**
* Return the buffer that APPLY DDL is written to for foreign keys and their associated indexes.
* <p>
@@ -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();
}
}
@@ -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);
}
@@ -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<AlterCmd> postProcessCommands(List<AlterCmd> cmds) {
List<AlterCmd> newCmds = new ArrayList<>();
Map<String, List<AlterCmd>> batches = new LinkedHashMap<>();
Set<String> columns = new HashSet<>();
for (AlterCmd cmd : cmds) {
switch (cmd.getOperation()) {
case "add":
case "alter":
case "drop":
if (cmd.getColumn() != null && !columns.add(cmd.getColumn())) {
// column already seen
flushBatches(newCmds, batches);
columns.clear();
}
batches.computeIfAbsent(cmd.getOperation(), k -> new ArrayList<>()).add(cmd);
break;
default:
flushBatches(newCmds, batches);
columns.clear();
newCmds.add(cmd);
}
}
flushBatches(newCmds, batches);
return newCmds;
}
/**
* Merges add/alter/drop commands into one statement.
*/
private void flushBatches(List<AlterCmd> newCmds, Map<String, List<AlterCmd>> batches) {
for (Entry<String, List<AlterCmd>> entry : batches.entrySet()) {
AlterCmd raw = newRawCommand("alter table ").append(tableName()).append(" ")
.append(entry.getKey()).append(" (");
List<AlterCmd> cmds = entry.getValue();
for (int i = 0; i < cmds.size(); i++) {
AlterCmd cmd = cmds.get(i);
if (i > 0) {
raw.append(",\n ");
}
raw.append(cmd.getColumn());
if (!cmd.getAlternation().isEmpty()) {
raw.append(" ").append(cmd.getAlternation());
}
}
raw.append(")");
newCmds.add(raw);
}
batches.clear();
}
}
}
@@ -0,0 +1,152 @@
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlAlterTable;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer;
/**
* Contains alter statements per table.
*
* @author Roland Praml, FOCONIS AG
*/
public class BaseAlterTableWrite implements DdlAlterTable {
protected static final String RAW_OPERATION = "$RAW";
public class AlterCmd {
// the command (e.g. "alter", "modify"
private final String operation;
// the affected column (note: each column can only be altered once on MariaDB)
private final String column;
private final DdlBuffer alternationBuffer = new BaseDdlBuffer() {
@Override
public DdlBuffer endOfStatement() {
throw new UnsupportedOperationException();
};
};
protected AlterCmd(String operation, String column) {
this.operation = operation;
this.column = column;
}
public AlterCmd append(String content) {
alternationBuffer.append(content);
return this;
}
public String getOperation() {
return operation;
}
public String getColumn() {
return column;
}
public String getAlternation() {
return alternationBuffer.getBuffer();
}
protected void write(Appendable target) throws IOException {
if (operation.equals(RAW_OPERATION)) {
// this is a raw command. e.g. an USP call. Must be done in the correct order
// of all alter commands
target.append(getAlternation());
} else {
target.append("alter table ").append(tableName).append(' ').append(operation);
if (column != null) {
target.append(' ').append(column);
}
if (!getAlternation().isEmpty()) {
target.append(' ').append(getAlternation());
}
}
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
try {
write(sb);
} catch (IOException e) {
throw new RuntimeException(e);
}
return sb.toString();
}
}
private final String tableName;
private List<AlterCmd> cmds = new ArrayList<>();
private boolean historyHandled;
public BaseAlterTableWrite(String tableName) {
this.tableName = tableName;
}
public String tableName() {
return tableName;
}
protected AlterCmd newRawCommand(String sql) {
AlterCmd cmd = new AlterCmd(RAW_OPERATION, null);
cmd.alternationBuffer.append(sql);
return cmd;
}
public AlterCmd newOperation(String operation, String column) {
return new AlterCmd(operation, column);
}
/**
* Adds a statement. The statement is prefixed with "alter table TABLENAME" and may be batched, if platform supports this. The
* returned StringBuilder can be used, to complete the statement
*/
@Override
public DdlBuffer append(String operation, String column) {
AlterCmd cmd = new AlterCmd(operation, column);
cmds.add(cmd);
return cmd.alternationBuffer;
}
@Override
public DdlBuffer raw(String sql) {
AlterCmd cmd = newRawCommand(sql);
cmds.add(cmd);
return cmd.alternationBuffer;
}
/**
* Method can be overwritten to return a new list of commands. The given list must not be modified, but a new command list with
* modified commands can be returned (e.g. to handle DB2 reorg or special syntax in Hana)
*/
protected List<AlterCmd> postProcessCommands(List<AlterCmd> cmds) {
return cmds;
}
/**
* Writes the DDL to <code>target</code>.
*/
@Override
public void write(Appendable target) throws IOException {
for (AlterCmd cmd : postProcessCommands(Collections.unmodifiableList(cmds))) {
cmd.write(target);
target.append(";\n");
}
}
@Override
public boolean isHistoryHandled() {
return historyHandled;
}
@Override
public void setHistoryHandled() {
historyHandled = true;
}
}
@@ -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;
}
@@ -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<UniqueConstraint> 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<String, HistoryTableUpdate> regenerateHistoryTriggers = new LinkedHashMap<>();
private final Set<String> 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<Column> columns, DdlIdentity identity) throws IOException {
private void writeTableColumns(DdlBuffer apply, List<Column> 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<Column> 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<Column> columns = addColumn.getColumn();
for (Column column : columns) {
alterTableAddColumn(writer.apply(), tableName, column, false, isTrue(addColumn.isWithHistory()));
alterTableAddColumn(writer, tableName, column, false, isTrue(addColumn.isWithHistory()));
}
if (isTrue(addColumn.isWithHistory()) && historySupport == HistorySupport.TRIGGER_BASED) {
if (isTrue(addColumn.isWithHistory())) {
platformDdl.regenerateHistoryTriggers(writer, tableName);
// make same changes to the history table
String historyTable = historyTable(tableName);
for (Column column : columns) {
regenerateHistoryTriggers(tableName, HistoryTableUpdate.Change.ADD, column.getName());
alterTableAddColumn(writer.apply(), historyTable, column, true, true);
if (alterHistoryTables) {
String historyTable = historyTable(tableName);
for (Column column : columns) {
alterTableAddColumn(writer, historyTable, column, true, true);
}
}
}
for (Column column : columns) {
@@ -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) {
@@ -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)");
}
/**
@@ -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
@@ -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;
@@ -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<AlterCmd> postProcessCommands(List<AlterCmd> cmds) {
List<AlterCmd> ret = new ArrayList<>(cmds.size() + 1);
boolean requiresReorg = false;
for (AlterCmd cmd : cmds) {
ret.add(cmd);
if (!requiresReorg && checkReorg(cmd)) {
requiresReorg = true;
}
}
if (requiresReorg) {
ret.add(newRawCommand("call sysproc.admin_cmd('reorg table " + tableName() + "')"));
}
return ret;
}
/**
* determine, if we need a reorg.
*
* See: https://www.ibm.com/docs/en/db2/11.5?topic=statements-alter-table The following is the full list of REORG-recommended
* ALTER statements that cause a version change and place the table into a REORG-pending state:
* <ul>
* <li>DROP COLUMN
* <li>ALTER COLUMN SET NOT NULL
* <li>ALTER COLUMN DROP NOT NULL
* <li>ALTER COLUMN SET DATA TYPE, except in the following situations:<br>
* Increasing the length of a VARCHAR or VARGRAPHIC column<br>
* Decreasing the length of a VARCHAR or VARGRAPHIC column without truncating trailing blanks from existing data, when no indexes
* exist on the column
* </ul>
*
*/
private boolean checkReorg(AlterCmd cmd) {
switch (cmd.getOperation()) {
case "drop column":
return true;
case "alter column":
String alter = cmd.getAlternation();
return alter.equals("set not null")
|| alter.equals("drop not default")
|| alter.startsWith("set data type"); // note: altering varchar length only is not detected here
default:
return false;
}
}
}
@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);
}
}
@@ -0,0 +1,138 @@
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
import java.util.Collection;
import io.ebean.config.DatabaseConfig;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlAlterTable;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite;
import io.ebeaninternal.dbmigration.migration.AddHistoryTable;
import io.ebeaninternal.dbmigration.migration.DropHistoryTable;
import io.ebeaninternal.dbmigration.model.MColumn;
import io.ebeaninternal.dbmigration.model.MTable;
/**
* DB2 History support.
*
* @author Roland Praml, FOCONIS AG
*/
public class Db2HistoryDdl implements PlatformHistoryDdl {
private String systemPeriodStart;
private String systemPeriodEnd;
private String transactionId;
private PlatformDdl platformDdl;
private String historySuffix;
@Override
public void configure(DatabaseConfig config, PlatformDdl platformDdl) {
this.systemPeriodStart = config.getAsOfSysPeriod() + "_start";
this.systemPeriodEnd = config.getAsOfSysPeriod() + "_end";
this.transactionId = config.getAsOfSysPeriod() + "_txn"; // required for DB2
this.platformDdl = platformDdl;
this.historySuffix = config.getHistoryTableSuffix();
}
@Override
public void createWithHistory(DdlWrite writer, MTable table) {
String tableName = table.getName();
String historyTableName = tableName + historySuffix;
DdlBuffer apply = writer.applyPostAlter();
apply.append(platformDdl.getCreateTableCommandPrefix()).append(" ").append(historyTableName).append(" (").newLine();
// create history table
Collection<MColumn> cols = table.allColumns();
for (MColumn column : cols) {
if (!column.isDraftOnly()) {
writeColumnDefinition(apply, column.getName(), column.getType(), column.isNotnull() || column.isPrimaryKey());
apply.append(",").newLine();
}
}
writeColumnDefinition(apply, systemPeriodStart, "timestamp(12)", true);
apply.append(",").newLine();
writeColumnDefinition(apply, systemPeriodEnd, "timestamp(12)", true);
apply.append(",").newLine();
writeColumnDefinition(apply, transactionId, "timestamp(12)", false);
apply.newLine().append(")").endOfStatement();
// enable system versioning
addSysPeriodColumns(writer, tableName);
enableSystemVersioning(apply, tableName);
platformDdl.alterTable(writer, tableName).setHistoryHandled();
// drop all: We do not drop columns here, as the whole table will be dropped
disableSystemVersioning(writer.dropAll(), tableName);
writer.dropAll().append("drop table ").append(historyTableName).endOfStatement();
}
void addSysPeriodColumns(DdlWrite writer, String baseTable) {
platformDdl.alterTableAddColumn(writer, baseTable, systemPeriodStart, "timestamp(12) not null generated always as row begin", null);
platformDdl.alterTableAddColumn(writer, baseTable, systemPeriodEnd, "timestamp(12) not null generated always as row end", null);
platformDdl.alterTableAddColumn(writer, baseTable, transactionId, "timestamp(12) generated always as transaction start id", null);
platformDdl.alterTable(writer, baseTable).append("add period system_time", null)
.append("(").append(systemPeriodStart).append(",").append(systemPeriodEnd).append(")");
}
@Override
public void dropHistoryTable(DdlWrite writer, DropHistoryTable dropHistoryTable) {
dropHistoryTable(writer, dropHistoryTable.getBaseTable(), dropHistoryTable.getBaseTable() + historySuffix);
}
protected void dropHistoryTable(DdlWrite writer, String baseTable, String historyTable) {
disableSystemVersioning(writer.apply(), baseTable);
writer.apply().append("alter table ").append(baseTable).append(" drop period system_time").endOfStatement();
// drop the period & period columns
platformDdl.alterTableDropColumn(writer, baseTable, systemPeriodStart);
platformDdl.alterTableDropColumn(writer, baseTable, systemPeriodEnd);
platformDdl.alterTableDropColumn(writer, baseTable, transactionId);
// drop the history table
writer.applyPostAlter().append("drop table ").append(historyTable).endOfStatement();
}
@Override
public void addHistoryTable(DdlWrite writer, AddHistoryTable addHistoryTable) {
MTable table = writer.getTable(addHistoryTable.getBaseTable());
if (table == null) {
throw new IllegalStateException("MTable " + addHistoryTable.getBaseTable() + " not found in writer? (required for history DDL)");
}
createWithHistory(writer, table);
}
@Override
public boolean alterHistoryTables() {
return true;
}
@Override
public void updateTriggers(DdlWrite writer, String tableName) {
DdlAlterTable alter = platformDdl.alterTable(writer, tableName);
MTable table = writer.getTable(tableName);
if (table.isWithHistory() && !alter.isHistoryHandled()) {
disableSystemVersioning(writer.apply(), tableName);
enableSystemVersioning(writer.applyPostAlter(), tableName);
alter.setHistoryHandled();
}
}
protected void writeColumnDefinition(DdlBuffer buffer, String columnName, String type, boolean isNotNull) {
String platformType = platformDdl.convert(type);
buffer.append(" ").append(platformDdl.lowerColumnName(columnName));
buffer.append(" ").append(platformType);
if (isNotNull) {
buffer.append(" not null");
}
}
public void disableSystemVersioning(DdlBuffer apply, String tableName) {
apply.append("alter table ").append(tableName).append(" drop versioning").endOfStatement();
}
public void enableSystemVersioning(DdlBuffer apply, String tableName) {
apply.append("alter table ").append(tableName).append(" add versioning use history table ").append(tableName).append(historySuffix).endOfStatement();
}
}
@@ -2,6 +2,7 @@ package io.ebeaninternal.dbmigration.ddlgeneration.platform;
import io.ebean.config.DatabaseConfig;
import io.ebean.config.DbConstraintNaming;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlAlterTable;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite;
import io.ebeaninternal.dbmigration.migration.AddHistoryTable;
@@ -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<String> columns = columnNamesForApply(table);
String baseTableName = table.getName();
String historyTableName = historyTableName(baseTableName);
return new DbTriggerUpdate(baseTableName, historyTableName, writer, columns);
}
@Override
public void dropHistoryTable(DdlWrite writer, DropHistoryTable dropHistoryTable) 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<String> 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<String> 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<String> columns) {
buffer.append(" insert into ").append(baseTable).append(historySuffix).append(" (").append(sysPeriodStart).append(",").append(sysPeriodEnd).append(",");
appendColumnNames(buffer, columns, "");
buffer.append(") values (OLD.").append(sysPeriodStart).append(", ").append(sysPeriodEndValue).append(",");
appendColumnNames(buffer, columns, "OLD.");
buffer.append(");").newLine();
}
void appendColumnNames(DdlBuffer buffer, List<String> columns, String columnPrefix) throws IOException {
void appendColumnNames(DdlBuffer buffer, List<String> 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;
}
}
@@ -1,71 +0,0 @@
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite;
import java.util.List;
/**
* DB trigger update when a change occurs on a table with history.
*/
public class DbTriggerUpdate {
private final String baseTableName;
private final String historyTableName;
private final DdlWrite writer;
private final List<String> columns;
public DbTriggerUpdate(String baseTableName, String historyTableName, DdlWrite writer, List<String> columns) {
this.baseTableName = baseTableName;
this.historyTableName = historyTableName;
this.writer = writer;
this.columns = columns;
}
/**
* Return the appropriate buffer for the current mode.
*/
public DdlBuffer historyViewBuffer() {
return writer.applyHistoryView();
}
/**
* Return the appropriate buffer for the current mode.
*/
public DdlBuffer historyTriggerBuffer() {
return writer.applyHistoryTrigger();
}
/**
* Return the appropriate drop dependency buffer for the current mode.
*/
public DdlBuffer dropDependencyBuffer() {
return writer.applyDropDependencies();
}
/**
* Return the base table name.
*/
public String getBaseTable() {
return baseTableName;
}
/**
* Return the history table name.
*/
public String getHistoryTable() {
return historyTableName;
}
/**
* Return the included columns.
*/
public List<String> getColumns() {
return columns;
}
}
@@ -1,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<String> 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
@@ -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();
}
}
@@ -1,11 +0,0 @@
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
import io.ebean.config.DatabaseConfig;
import io.ebeaninternal.dbmigration.ddlgeneration.BaseDdlHandler;
public class HanaDdlHandler extends BaseDdlHandler {
public HanaDdlHandler(DatabaseConfig config, PlatformDdl platformDdl) {
super(config, platformDdl, new HanaTableDdl(config, platformDdl));
}
}
@@ -1,6 +1,7 @@
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
import io.ebean.config.DatabaseConfig;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlAlterTable;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite;
import io.ebeaninternal.dbmigration.migration.AddHistoryTable;
@@ -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<String, String> 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);
}
}
@@ -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<Column> columns = addColumn.getColumn();
for (Column column : columns) {
alterTableAddColumn(writer.apply(), historyTable, column, true, true);
}
historyDdl.enableSystemVersioning(writer.apply(), table.getName(), historyTable, false, this.generateUniqueDdl);
}
}
@Override
public void generate(DdlWrite writer, AlterColumn alterColumn) 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);
}
}
}
@@ -1,79 +0,0 @@
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
import java.util.ArrayList;
import java.util.List;
/**
* Bean holding comments relating to a history table that needs to have it's
* associated triggers/function updated due to columns added/dropped/included
* or excluded.
*/
public class HistoryTableUpdate {
/**
* Column change type.
*/
public enum Change {
ADD,
DROP,
INCLUDE,
EXCLUDE,
ALTER
}
private static class Column {
final Change change;
final String column;
public Column(Change change, String column) {
this.change = change;
this.column = column;
}
@Override
public String toString() {
return description();
}
public String description() {
return change.name().toLowerCase() + " " + column;
}
}
private final String baseTable;
private final List<Column> columnChanges = new ArrayList<>();
/**
* Construct with a given base table name.
*/
public HistoryTableUpdate(String baseTable) {
this.baseTable = baseTable;
}
/**
* Return a description of the changes that cause the history trigger/function
* to be regenerated (added, included, excluded and dropped columns).
*/
public String description() {
return columnChanges.toString();
}
/**
* Add a comment for column added, dropped, included or excluded.
*/
public void add(Change change, String column) {
columnChanges.add(new Column(change, column));
}
/**
* Return the base table name.
*/
public String getBaseTable() {
return baseTable;
}
}
@@ -1,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();
}
}
}
@@ -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<String> 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<String> tables) throws IOException {
buffer.append("unlock tables").endOfStatement();
}
}
@@ -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<String> columnNames) {
recreateHistoryView(update);
buffer.append("lock tables ").append(baseTable).append(" write").endOfStatement();
addBeforeUpdate(buffer, updateTriggerName(baseTable), baseTable, columnNames);
addBeforeDelete(buffer, deleteTriggerName(baseTable), baseTable, columnNames);
buffer.appendStatement("unlock tables");
DdlBuffer buffer = update.historyTriggerBuffer();
String baseTable = update.getBaseTable();
dropTriggers(buffer, baseTable);
addBeforeUpdate(updateTriggerName(baseTable), update);
addBeforeDelete(deleteTriggerName(baseTable), update);
}
private void addBeforeUpdate(String triggerName, DbTriggerUpdate update) throws IOException {
private void addBeforeUpdate(DdlBuffer apply, String triggerName, String tableName, List<String> columnNames) {
DdlBuffer apply = update.historyTriggerBuffer();
apply
.append("delimiter $$").newLine()
.append("create trigger ").append(triggerName).append(" before update on ").append(update.getBaseTable())
.append("create trigger ").append(triggerName).append(" before update on ").append(tableName)
.append(" for each row begin").newLine();
appendInsertIntoHistory(apply, update.getHistoryTable(), update.getColumns());
appendInsertIntoHistory(apply, tableName, columnNames);
apply
.append(" set NEW.").append(sysPeriod).append("_start = now(6)").endOfStatement()
.append("end$$").newLine();
}
private void addBeforeDelete(String triggerName, DbTriggerUpdate update) throws IOException {
private void addBeforeDelete(DdlBuffer apply, String triggerName, String tableName, List<String> columnNames) {
DdlBuffer apply = update.historyTriggerBuffer();
apply
.append("delimiter $$").newLine()
.append("create trigger ").append(triggerName).append(" before delete on ").append(update.getBaseTable())
.append("create trigger ").append(triggerName).append(" before delete on ").append(tableName)
.append(" for each row begin").newLine();
appendInsertIntoHistory(apply, update.getHistoryTable(), update.getColumns());
appendInsertIntoHistory(apply, tableName, columnNames);
apply.append("end$$").newLine();
}
@@ -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
}
}
@@ -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<String> columnNames) {
DbTriggerUpdate update = createDbTriggerUpdate(writer, table);
addBeforeUpdate(updateTriggerName(update.getBaseTable()), update);
addBeforeDelete(deleteTriggerName(update.getBaseTable()), update);
addBeforeUpdate(buffer, updateTriggerName(baseTable), baseTable, columnNames);
addBeforeDelete(buffer, deleteTriggerName(baseTable), baseTable, columnNames);
}
@Override
protected void updateHistoryTriggers(DbTriggerUpdate update) throws IOException {
private void addBeforeUpdate(DdlBuffer apply, String triggerName, String tableName, List<String> columnNames) {
recreateHistoryView(update);
addTriggerStart(triggerName, tableName, apply, " before update for each row as ");
DdlBuffer buffer = update.historyTriggerBuffer();
String baseTable = update.getBaseTable();
dropTriggers(buffer, baseTable);
addBeforeUpdate(updateTriggerName(baseTable), update);
addBeforeDelete(deleteTriggerName(baseTable), update);
}
private void addBeforeUpdate(String triggerName, DbTriggerUpdate update) 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<String> columnNames) {
DdlBuffer apply = update.historyTriggerBuffer();
addTriggerStart(triggerName, update, apply, " before delete for each row as");
appendInsertIntoHistory(apply, update.getHistoryTable(), update.getColumns());
addTriggerStart(triggerName, tableName, apply, " before delete for each row as");
appendInsertIntoHistory(apply, tableName, columnNames);
addEndTrigger(apply);
}
private void addTriggerStart(String triggerName, DbTriggerUpdate update, DdlBuffer apply, String s) 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()
@@ -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();
}
}
@@ -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<Column> columns, DdlIdentity identity) throws IOException {
public void writeTableColumns(DdlBuffer apply, List<Column> 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.
* <p>
* Note that that MySql and SQL Server instead use alterColumnBaseAttributes()
* Note that that MySql, SQL Server, and HANA instead use alterColumn()
* </p>
*/
public String alterColumnType(String tableName, String columnName, String type) {
return "alter table " + tableName + " " + alterColumn + " " + columnName + " " + columnSetType + convert(type) + alterColumnSuffix;
protected void alterColumnType(DdlWrite writer, AlterColumn alter) {
alterTable(writer, alter.getTableName()).append(alterColumn, alter.getColumnName())
.append(columnSetType).append(convert(alter.getType()));
}
/**
* Alter a column adding or removing the not null constraint.
* <p>
* Note that that MySql, SQL Server, and HANA instead use alterColumnBaseAttributes()
* Note that that MySql, SQL Server, and HANA instead use alterColumn()
* </p>
*/
public String alterColumnNotnull(String tableName, String columnName, boolean notnull) {
String suffix = notnull ? columnSetNotnull : columnSetNull;
return "alter table " + tableName + " " + alterColumn + " " + columnName + " " + suffix + alterColumnSuffix;
protected void alterColumnNotnull(DdlWrite writer, AlterColumn alter) {
DdlBuffer buffer = alterTable(writer, alter.getTableName()).append(alterColumn, alter.getColumnName());
if (Boolean.TRUE.equals(alter.isNotnull())) {
buffer.append(columnSetNotnull);
} else {
buffer.append(columnSetNull);
}
}
/**
@@ -631,22 +624,46 @@ public class PlatformDdl {
/**
* Alter column setting the default value.
* <p>
* Note that that MySql, SQL Server, and HANA instead use alterColumn()
* </p>
*/
public String alterColumnDefaultValue(String tableName, String columnName, String defaultValue) {
String suffix = DdlHelp.isDropDefault(defaultValue) ? columnDropDefault : columnSetDefault + " " + convertDefaultValue(defaultValue);
return "alter table " + tableName + " " + alterColumn + " " + columnName + " " + suffix + alterColumnSuffix;
protected void alterColumnDefault(DdlWrite writer, AlterColumn alter) {
DdlBuffer buffer = alterTable(writer, alter.getTableName()).append(alterColumn, alter.getColumnName());
if (DdlHelp.isDropDefault(alter.getDefaultValue())) {
buffer.append(columnDropDefault);
} else {
buffer.append(columnSetDefault).appendWithSpace(convertDefaultValue(alter.getDefaultValue()));
}
}
/**
* Alter column setting both the type and not null constraint.
* Alter column setting (type, default value and not null constraint).
* <p>
* Used by MySql, SQL Server, and HANA as these require both column attributes to be set together.
* Used by MySql, SQL Server, and HANA as these require all column attributes to
* be set together.
* </p>
*/
public String alterColumnBaseAttributes(AlterColumn alter) {
// by default do nothing, only used by mysql, sql server, and HANA as they can only
// modify the column with the full column definition
return null;
public void alterColumn(DdlWrite writer, AlterColumn alter) {
if (hasValue(alter.getType())) {
alterColumnType(writer, alter);
}
if (hasValue(alter.getDefaultValue())) {
alterColumnDefault(writer, alter);
}
if (alter.isNotnull() != null) {
alterColumnNotnull(writer, alter);
}
}
/**
* Creates or replace a new DdlAlterTable for given tableName.
*/
protected DdlAlterTable alterTable(DdlWrite writer, String tableName) {
return writer.applyAlterTable(tableName, BaseAlterTableWrite::new);
}
protected void appendColumns(String[] columns, StringBuilder buffer) {
@@ -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<String> tables) throws IOException {
}
/**
* Mysql-specific: Unlocks all tables for triggers that have to be updated.
*/
public void unlockTables(DdlBuffer buffer, Collection<String> 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
}
@@ -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
}
}
@@ -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);
}
}
@@ -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<String> columnNames) {
String procedureName = procedureName(baseTableName);
String triggerName = triggerName(baseTableName);
DdlBuffer apply = writer.applyHistoryTrigger();
apply
createOrReplaceFunction(buffer, procedureName, historyTableName(baseTableName), columnNames);
buffer
.append("create trigger ").append(triggerName).newLine()
.append(" before update or delete on ").append(baseTableName).newLine()
.append(" for each row execute procedure ").append(procedureName).append("();").newLine().newLine();
}
@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<String> includedColumns) throws IOException {
protected void createOrReplaceFunction(DdlBuffer apply, String procedureName, String historyTable, List<String> 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<String> 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<String> columns) throws IOException {
protected void appendInsertIntoHistory(DdlBuffer buffer, String historyTable, List<String> columns) {
buffer.append(" insert into ").append(historyTable).append(" (").append(sysPeriod).append(",");
appendColumnNames(buffer, columns, "");
buffer.append(") values (tstzrange(lowerTs,upperTs), ");
@@ -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<AlterCmd> postProcessCommands(List<AlterCmd> cmds) {
List<AlterCmd> ret = new ArrayList<AlterCmd>();
for (AlterCmd cmd : cmds) {
switch (cmd.getOperation()) {
case "alter column":
ret.add(newRawCommand("-- not supported: " + cmd));
break;
default:
ret.add(cmd);
}
}
return ret;
}
}
}
@@ -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);
}
}
@@ -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();
}
}
@@ -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();
@@ -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);
}
}
@@ -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) {
@@ -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<ChangeSet> 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);
}
/**
@@ -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);
}
}
@@ -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"));
@@ -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();
}
}
@@ -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());
}
}
@@ -1,32 +0,0 @@
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
public class HistoryTableUpdateTest {
@Test
public void testDescription() throws Exception {
HistoryTableUpdate upd = new HistoryTableUpdate("mytab");
upd.add(HistoryTableUpdate.Change.ADD, "two");
upd.add(HistoryTableUpdate.Change.DROP, "four");
assertThat(upd.description()).isEqualTo("[add two, drop four]");
}
@Test
public void testDescription_withIncludeExclude() throws Exception {
HistoryTableUpdate upd = new HistoryTableUpdate("mytab");
upd.add(HistoryTableUpdate.Change.ADD, "two");
upd.add(HistoryTableUpdate.Change.INCLUDE, "five");
upd.add(HistoryTableUpdate.Change.EXCLUDE, "six");
upd.add(HistoryTableUpdate.Change.DROP, "four");
assertThat(upd.description()).isEqualTo("[add two, include five, exclude six, drop four]");
}
}
@@ -3,6 +3,7 @@ package io.ebeaninternal.dbmigration.ddlgeneration.platform;
import io.ebean.DB;
import io.ebean.config.DatabaseConfig;
import io.ebean.config.dbplatform.IdType;
import io.ebean.config.dbplatform.db2.DB2LuwPlatform;
import io.ebean.config.dbplatform.h2.H2Platform;
import io.ebean.config.dbplatform.hana.HanaPlatform;
import io.ebean.config.dbplatform.mysql.MySqlPlatform;
@@ -14,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();
}
@@ -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");
@@ -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);
@@ -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;
@@ -1,3 +1,4 @@
-- apply changes
create table foo (
col1 varchar(4) generated by default as identity not null,
col2 varchar(30) not null,
@@ -6,7 +7,6 @@ create table foo (
);
comment on table foo is 'comment';
-- apply alter tables
alter table foo add column added_to_foo varchar(20);
alter table foo drop column col2;
@@ -1,2 +1,3 @@
-- drop all
drop table if exists foo;
@@ -21,6 +21,7 @@ create table ckey_parent (
constraint pk_ckey_parent primary key (one_key,two_key)
);
-- foreign keys and indices
create index ix_ckey_detail_parent on ckey_detail (one_key,two_key);
alter table ckey_detail add constraint fk_ckey_detail_parent foreign key (one_key,two_key) references ckey_parent (one_key,two_key) on delete restrict on update restrict;
@@ -13,6 +13,7 @@ create table phones (
constraint pk_phones primary key (id)
);
-- foreign keys and indices
create index ix_phones_person_id on phones (person_id);
alter table phones add constraint fk_phones_person_id foreign key (person_id) references persons (id) on delete restrict on update restrict;

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