cn.lead2success.ddlutils.platform.PlatformImplBase Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ddlutils Show documentation
Show all versions of ddlutils Show documentation
Fork of Apache DdlUtils project without ant support.
package cn.lead2success.ddlutils.platform;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
import cn.lead2success.ddlutils.DatabaseOperationException;
import cn.lead2success.ddlutils.DdlUtilsException;
import cn.lead2success.ddlutils.Platform;
import cn.lead2success.ddlutils.PlatformInfo;
import cn.lead2success.ddlutils.alteration.*;
import cn.lead2success.ddlutils.dynabean.SqlDynaClass;
import cn.lead2success.ddlutils.dynabean.SqlDynaProperty;
import cn.lead2success.ddlutils.model.*;
import cn.lead2success.ddlutils.util.JdbcSupport;
import cn.lead2success.ddlutils.util.SqlTokenizer;
import org.apache.commons.beanutils.DynaBean;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.Predicate;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.io.IOException;
import java.io.StringWriter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.sql.*;
import java.util.*;
/**
* Base class for platform implementations.
*
* @version $Revision: 231110 $
*/
public abstract class PlatformImplBase extends JdbcSupport implements Platform {
/** The default name for models read from the database, if no name as given. */
protected static final String MODEL_DEFAULT_NAME = "default";
/** The log for this platform. */
private final Log _log = LogFactory.getLog(getClass());
/** The platform info. */
private PlatformInfo _info = new PlatformInfo();
/** The sql builder for this platform. */
private SqlBuilder _builder;
/** The model reader for this platform. */
private JdbcModelReader _modelReader;
/** Whether script mode is on. */
private boolean _scriptModeOn;
/** Whether SQL comments are generated or not. */
private boolean _sqlCommentsOn = true;
/** Whether delimited identifiers are used or not. */
private boolean _delimitedIdentifierModeOn;
/** Whether identity override is enabled. */
private boolean _identityOverrideOn;
/** Whether read foreign keys shall be sorted alphabetically. */
private boolean _foreignKeysSorted;
/**
* Whether to use the default ON UPDATE action if the specified one is
* unsupported.
*/
private boolean _useDefaultOnUpdateActionIfUnsupported = true;
/**
* Whether to use the default ON DELETE action if the specified one is
* unsupported.
*/
private boolean _useDefaultOnDeleteActionIfUnsupported = true;
/**
* {@inheritDoc}
*/
public SqlBuilder getSqlBuilder() {
return _builder;
}
/**
* Sets the sql builder for this platform.
*
* @param builder
* The sql builder
*/
protected void setSqlBuilder(SqlBuilder builder) {
_builder = builder;
}
/**
* {@inheritDoc}
*/
public JdbcModelReader getModelReader() {
if (_modelReader == null) {
_modelReader = new JdbcModelReader(this);
}
return _modelReader;
}
/**
* Sets the model reader for this platform.
*
* @param modelReader
* The model reader
*/
protected void setModelReader(JdbcModelReader modelReader) {
_modelReader = modelReader;
}
/**
* {@inheritDoc}
*/
public PlatformInfo getPlatformInfo() {
return _info;
}
/**
* {@inheritDoc}
*/
public boolean isScriptModeOn() {
return _scriptModeOn;
}
/**
* {@inheritDoc}
*/
public void setScriptModeOn(boolean scriptModeOn) {
_scriptModeOn = scriptModeOn;
}
/**
* {@inheritDoc}
*/
public boolean isSqlCommentsOn() {
return _sqlCommentsOn;
}
/**
* {@inheritDoc}
*/
public void setSqlCommentsOn(boolean sqlCommentsOn) {
if (!getPlatformInfo().isSqlCommentsSupported() && sqlCommentsOn) {
throw new DdlUtilsException("Platform " + getName() + " does not support SQL comments");
}
_sqlCommentsOn = sqlCommentsOn;
}
/**
* {@inheritDoc}
*/
public boolean isDelimitedIdentifierModeOn() {
return _delimitedIdentifierModeOn;
}
/**
* {@inheritDoc}
*/
public void setDelimitedIdentifierModeOn(boolean delimitedIdentifierModeOn) {
if (!getPlatformInfo().isDelimitedIdentifiersSupported() && delimitedIdentifierModeOn) {
throw new DdlUtilsException("Platform " + getName() + " does not support delimited identifier");
}
_delimitedIdentifierModeOn = delimitedIdentifierModeOn;
}
/**
* {@inheritDoc}
*/
public boolean isIdentityOverrideOn() {
return _identityOverrideOn;
}
/**
* {@inheritDoc}
*/
public void setIdentityOverrideOn(boolean identityOverrideOn) {
_identityOverrideOn = identityOverrideOn;
}
/**
* {@inheritDoc}
*/
public boolean isForeignKeysSorted() {
return _foreignKeysSorted;
}
/**
* {@inheritDoc}
*/
public void setForeignKeysSorted(boolean foreignKeysSorted) {
_foreignKeysSorted = foreignKeysSorted;
}
/**
* {@inheritDoc}
*/
public boolean isDefaultOnUpdateActionUsedIfUnsupported() {
return _useDefaultOnUpdateActionIfUnsupported;
}
/**
* {@inheritDoc}
*/
public void setDefaultOnUpdateActionUsedIfUnsupported(boolean useDefault) {
_useDefaultOnUpdateActionIfUnsupported = useDefault;
}
/**
* {@inheritDoc}
*/
public boolean isDefaultOnDeleteActionUsedIfUnsupported() {
return _useDefaultOnDeleteActionIfUnsupported;
}
/**
* {@inheritDoc}
*/
public void setDefaultOnDeleteActionUsedIfUnsupported(boolean useDefault) {
_useDefaultOnDeleteActionIfUnsupported = useDefault;
}
/**
* Returns the log for this platform.
*
* @return The log
*/
protected Log getLog() {
return _log;
}
/**
* Logs any warnings associated to the given connection. Note that the
* connection needs to be open for this.
*
* @param connection
* The open connection
*/
protected void logWarnings(Connection connection) throws SQLException {
SQLWarning warning = connection.getWarnings();
while (warning != null) {
getLog().warn(warning.getLocalizedMessage(), warning.getCause());
warning = warning.getNextWarning();
}
}
/**
* {@inheritDoc}
*/
public int evaluateBatch(String sql, boolean continueOnError) throws DatabaseOperationException {
Connection connection = borrowConnection();
try {
return evaluateBatch(connection, sql, continueOnError);
} finally {
returnConnection(connection);
}
}
/**
* {@inheritDoc}
*/
public int evaluateBatch(Connection connection, String sql, boolean continueOnError)
throws DatabaseOperationException {
Statement statement = null;
int errors = 0;
int commandCount = 0;
// we tokenize the SQL along the delimiters, and we also make sure that only
// delimiters
// at the end of a line or the end of the string are used (row mode)
try {
statement = connection.createStatement();
SqlTokenizer tokenizer = new SqlTokenizer(sql);
while (tokenizer.hasMoreStatements()) {
String command = tokenizer.getNextStatement();
// ignore whitespace
command = command.trim();
if (command.length() == 0) {
continue;
}
commandCount++;
if (_log.isDebugEnabled()) {
_log.debug("About to execute SQL " + command);
}
try {
int results = statement.executeUpdate(command);
if (_log.isDebugEnabled()) {
_log.debug("After execution, " + results + " row(s) have been changed");
}
} catch (SQLException ex) {
if (continueOnError) {
// Since the user deciced to ignore this error, we log the error
// on level warn, and the exception itself on level debug
_log.warn("SQL Command " + command + " failed with: " + ex.getMessage());
if (_log.isDebugEnabled()) {
_log.debug(ex);
}
errors++;
} else {
throw new DatabaseOperationException("Error while executing SQL " + command, ex);
}
}
// lets display any warnings
SQLWarning warning = connection.getWarnings();
while (warning != null) {
_log.warn(warning.toString());
warning = warning.getNextWarning();
}
connection.clearWarnings();
}
_log.info("Executed " + commandCount + " SQL command(s) with " + errors + " error(s)");
} catch (SQLException ex) {
throw new DatabaseOperationException("Error while executing SQL", ex);
} finally {
closeStatement(statement);
}
return errors;
}
/**
* {@inheritDoc}
*/
public void shutdownDatabase() throws DatabaseOperationException {
Connection connection = borrowConnection();
try {
shutdownDatabase(connection);
} finally {
returnConnection(connection);
}
}
/**
* {@inheritDoc}
*/
public void shutdownDatabase(Connection connection) throws DatabaseOperationException {
// Per default do nothing as most databases don't need this
}
/**
* {@inheritDoc}
*/
public void createDatabase(String jdbcDriverClassName, String connectionUrl, String username, String password,
Map parameters) throws DatabaseOperationException, UnsupportedOperationException {
throw new UnsupportedOperationException(
"Database creation is not supported for the database platform " + getName());
}
/**
* {@inheritDoc}
*/
public void dropDatabase(String jdbcDriverClassName, String connectionUrl, String username, String password)
throws DatabaseOperationException, UnsupportedOperationException {
throw new UnsupportedOperationException(
"Database deletion is not supported for the database platform " + getName());
}
/**
* {@inheritDoc}
*/
public void createTables(Database model, boolean dropTablesFirst, boolean continueOnError)
throws DatabaseOperationException {
createModel(model, dropTablesFirst, continueOnError);
}
/**
* {@inheritDoc}
*/
public void createTables(Database model, CreationParameters params, boolean dropTablesFirst,
boolean continueOnError) throws DatabaseOperationException {
createModel(model, params, dropTablesFirst, continueOnError);
}
/**
* {@inheritDoc}
*/
public void createTables(Connection connection, Database model, boolean dropTablesFirst, boolean continueOnError)
throws DatabaseOperationException {
createModel(connection, model, dropTablesFirst, continueOnError);
}
/**
* {@inheritDoc}
*/
public void createTables(Connection connection, Database model, CreationParameters params, boolean dropTablesFirst,
boolean continueOnError) throws DatabaseOperationException {
createModel(connection, model, params, dropTablesFirst, continueOnError);
}
/**
* {@inheritDoc}
*/
public String getCreateTablesSql(Database model, boolean dropTablesFirst, boolean continueOnError) {
return getCreateModelSql(model, dropTablesFirst, continueOnError);
}
/**
* {@inheritDoc}
*/
public String getCreateTablesSql(Database model, CreationParameters params, boolean dropTablesFirst,
boolean continueOnError) {
return getCreateModelSql(model, params, dropTablesFirst, continueOnError);
}
/**
* {@inheritDoc}
*/
public void createModel(Database model, boolean dropTablesFirst, boolean continueOnError)
throws DatabaseOperationException {
Connection connection = borrowConnection();
try {
createModel(connection, model, dropTablesFirst, continueOnError);
} finally {
returnConnection(connection);
}
}
/**
* {@inheritDoc}
*/
public void createModel(Connection connection, Database model, boolean dropTablesFirst, boolean continueOnError)
throws DatabaseOperationException {
String sql = getCreateModelSql(model, dropTablesFirst, continueOnError);
evaluateBatch(connection, sql, continueOnError);
}
/**
* {@inheritDoc}
*/
public void createModel(Database model, CreationParameters params, boolean dropTablesFirst, boolean continueOnError)
throws DatabaseOperationException {
Connection connection = borrowConnection();
try {
createModel(connection, model, params, dropTablesFirst, continueOnError);
} finally {
returnConnection(connection);
}
}
/**
* {@inheritDoc}
*/
public void createModel(Connection connection, Database model, CreationParameters params, boolean dropTablesFirst,
boolean continueOnError) throws DatabaseOperationException {
String sql = getCreateModelSql(model, params, dropTablesFirst, continueOnError);
evaluateBatch(connection, sql, continueOnError);
}
/**
* {@inheritDoc}
*/
public String getCreateModelSql(Database model, boolean dropTablesFirst, boolean continueOnError) {
String sql = null;
try {
StringWriter buffer = new StringWriter();
getSqlBuilder().setWriter(buffer);
getSqlBuilder().createTables(model, dropTablesFirst);
sql = buffer.toString();
} catch (IOException e) {
// won't happen because we're using a string writer
}
return sql;
}
/**
* {@inheritDoc}
*/
public String getCreateModelSql(Database model, CreationParameters params, boolean dropTablesFirst,
boolean continueOnError) {
String sql = null;
try {
StringWriter buffer = new StringWriter();
getSqlBuilder().setWriter(buffer);
getSqlBuilder().createTables(model, params, dropTablesFirst);
sql = buffer.toString();
} catch (IOException e) {
// won't happen because we're using a string writer
}
return sql;
}
/**
* Returns the model comparator to be used for this platform. This method is
* intendeded to be redefined by platforms that need to customize the model
* reader.
*
* @return The model comparator
*/
protected ModelComparator getModelComparator() {
return new ModelComparator(getPlatformInfo(), getTableDefinitionChangesPredicate(),
isDelimitedIdentifierModeOn());
}
/**
* Returns the predicate that defines which changes are supported by the
* platform.
*
* @return The predicate
*/
protected TableDefinitionChangesPredicate getTableDefinitionChangesPredicate() {
return new DefaultTableDefinitionChangesPredicate();
}
/**
* {@inheritDoc}
*/
public List getChanges(Database currentModel, Database desiredModel) {
List changes = getModelComparator().compare(currentModel, desiredModel);
return sortChanges(changes);
}
/**
* Sorts the changes so that they can be executed by the database. E.g. tables
* need to be created before they can be referenced by foreign keys, indexes
* should be dropped before a table is dropped etc.
*
* @param changes
* The original changes
* @return The sorted changes - this can be the original list object or a new
* one
*/
protected List sortChanges(List changes) {
final Map, Integer> typeOrder = new HashMap<>();
typeOrder.put(RemoveForeignKeyChange.class, new Integer(0));
typeOrder.put(RemoveIndexChange.class, new Integer(1));
typeOrder.put(RemoveTableChange.class, new Integer(2));
typeOrder.put(RecreateTableChange.class, new Integer(3));
typeOrder.put(RemovePrimaryKeyChange.class, new Integer(3));
typeOrder.put(RemoveColumnChange.class, new Integer(4));
typeOrder.put(ColumnDefinitionChange.class, new Integer(5));
typeOrder.put(ColumnOrderChange.class, new Integer(5));
typeOrder.put(AddColumnChange.class, new Integer(5));
typeOrder.put(PrimaryKeyChange.class, new Integer(5));
typeOrder.put(AddPrimaryKeyChange.class, new Integer(6));
typeOrder.put(AddTableChange.class, new Integer(7));
typeOrder.put(AddIndexChange.class, new Integer(8));
typeOrder.put(AddForeignKeyChange.class, new Integer(9));
Collections.sort(changes, new Comparator() {
public int compare(ModelChange objA, ModelChange objB) {
Integer orderValueA = (Integer) typeOrder.get(objA.getClass());
Integer orderValueB = (Integer) typeOrder.get(objB.getClass());
if (orderValueA == null) {
return (orderValueB == null ? 0 : 1);
} else if (orderValueB == null) {
return -1;
} else {
return orderValueA.compareTo(orderValueB);
}
}
});
return changes;
}
/**
* {@inheritDoc}
*/
public void alterTables(Database desiredModel, boolean continueOnError) throws DatabaseOperationException {
Connection connection = borrowConnection();
try {
Database currentModel = readModelFromDatabase(connection, desiredModel.getName());
alterModel(currentModel, desiredModel, continueOnError);
} finally {
returnConnection(connection);
}
}
/**
* {@inheritDoc}
*/
public void alterTables(Database desiredModel, CreationParameters params, boolean continueOnError)
throws DatabaseOperationException {
Connection connection = borrowConnection();
try {
Database currentModel = readModelFromDatabase(connection, desiredModel.getName());
alterModel(currentModel, desiredModel, params, continueOnError);
} finally {
returnConnection(connection);
}
}
/**
* {@inheritDoc}
*/
public void alterTables(String catalog, String schema, String[] tableTypes, Database desiredModel,
boolean continueOnError) throws DatabaseOperationException {
Connection connection = borrowConnection();
try {
Database currentModel = readModelFromDatabase(connection, desiredModel.getName(), catalog, schema,
tableTypes);
alterModel(currentModel, desiredModel, continueOnError);
} finally {
returnConnection(connection);
}
}
/**
* {@inheritDoc}
*/
public void alterTables(String catalog, String schema, String[] tableTypes, Database desiredModel,
CreationParameters params, boolean continueOnError) throws DatabaseOperationException {
Connection connection = borrowConnection();
try {
Database currentModel = readModelFromDatabase(connection, desiredModel.getName(), catalog, schema,
tableTypes);
alterModel(currentModel, desiredModel, params, continueOnError);
} finally {
returnConnection(connection);
}
}
/**
* {@inheritDoc}
*/
public void alterTables(Connection connection, Database desiredModel, boolean continueOnError)
throws DatabaseOperationException {
Database currentModel = readModelFromDatabase(connection, desiredModel.getName());
alterModel(currentModel, desiredModel, continueOnError);
}
/**
* {@inheritDoc}
*/
public void alterTables(Connection connection, Database desiredModel, CreationParameters params,
boolean continueOnError) throws DatabaseOperationException {
Database currentModel = readModelFromDatabase(connection, desiredModel.getName());
alterModel(currentModel, desiredModel, params, continueOnError);
}
/**
* {@inheritDoc}
*/
public void alterTables(Connection connection, String catalog, String schema, String[] tableTypes,
Database desiredModel, boolean continueOnError) throws DatabaseOperationException {
Database currentModel = readModelFromDatabase(connection, desiredModel.getName(), catalog, schema, tableTypes);
alterModel(currentModel, desiredModel, continueOnError);
}
/**
* {@inheritDoc}
*/
public void alterTables(Connection connection, String catalog, String schema, String[] tableTypes,
Database desiredModel, CreationParameters params, boolean continueOnError)
throws DatabaseOperationException {
Database currentModel = readModelFromDatabase(connection, desiredModel.getName(), catalog, schema, tableTypes);
alterModel(currentModel, desiredModel, params, continueOnError);
}
/**
* {@inheritDoc}
*/
public String getAlterTablesSql(Database desiredModel) throws DatabaseOperationException {
Connection connection = borrowConnection();
try {
Database currentModel = readModelFromDatabase(connection, desiredModel.getName());
return getAlterModelSql(currentModel, desiredModel);
} finally {
returnConnection(connection);
}
}
/**
* {@inheritDoc}
*/
public String getAlterTablesSql(Database desiredModel, CreationParameters params)
throws DatabaseOperationException {
Connection connection = borrowConnection();
try {
Database currentModel = readModelFromDatabase(connection, desiredModel.getName());
return getAlterModelSql(currentModel, desiredModel, params);
} finally {
returnConnection(connection);
}
}
/**
* {@inheritDoc}
*/
public String getAlterTablesSql(String catalog, String schema, String[] tableTypes, Database desiredModel)
throws DatabaseOperationException {
Connection connection = borrowConnection();
try {
Database currentModel = readModelFromDatabase(connection, desiredModel.getName(), catalog, schema,
tableTypes);
return getAlterModelSql(currentModel, desiredModel);
} finally {
returnConnection(connection);
}
}
/**
* {@inheritDoc}
*/
public String getAlterTablesSql(String catalog, String schema, String[] tableTypes, Database desiredModel,
CreationParameters params) throws DatabaseOperationException {
Connection connection = borrowConnection();
try {
Database currentModel = readModelFromDatabase(connection, desiredModel.getName(), catalog, schema,
tableTypes);
return getAlterModelSql(currentModel, desiredModel, params);
} finally {
returnConnection(connection);
}
}
/**
* {@inheritDoc}
*/
public String getAlterTablesSql(Connection connection, Database desiredModel) throws DatabaseOperationException {
Database currentModel = readModelFromDatabase(connection, desiredModel.getName());
return getAlterModelSql(currentModel, desiredModel);
}
/**
* {@inheritDoc}
*/
public String getAlterTablesSql(Connection connection, Database desiredModel, CreationParameters params)
throws DatabaseOperationException {
Database currentModel = readModelFromDatabase(connection, desiredModel.getName());
return getAlterModelSql(currentModel, desiredModel, params);
}
/**
* {@inheritDoc}
*/
public String getAlterTablesSql(Connection connection, String catalog, String schema, String[] tableTypes,
Database desiredModel) throws DatabaseOperationException {
Database currentModel = readModelFromDatabase(connection, desiredModel.getName(), catalog, schema, tableTypes);
return getAlterModelSql(currentModel, desiredModel);
}
/**
* {@inheritDoc}
*/
public String getAlterTablesSql(Connection connection, String catalog, String schema, String[] tableTypes,
Database desiredModel, CreationParameters params) throws DatabaseOperationException {
Database currentModel = readModelFromDatabase(connection, desiredModel.getName(), catalog, schema, tableTypes);
return getAlterModelSql(currentModel, desiredModel, params);
}
/**
* {@inheritDoc}
*/
public String getAlterModelSql(Database currentModel, Database desiredModel) throws DatabaseOperationException {
return getAlterModelSql(currentModel, desiredModel, null);
}
/**
* {@inheritDoc}
*/
public String getAlterModelSql(Database currentModel, Database desiredModel, CreationParameters params)
throws DatabaseOperationException {
List changes = getChanges(currentModel, desiredModel);
String sql = null;
try {
StringWriter buffer = new StringWriter();
getSqlBuilder().setWriter(buffer);
processChanges(currentModel, changes, params);
sql = buffer.toString();
} catch (IOException ex) {
// won't happen because we're using a string writer
}
return sql;
}
/**
* {@inheritDoc}
*/
public void alterModel(Database currentModel, Database desiredModel, boolean continueOnError)
throws DatabaseOperationException {
Connection connection = borrowConnection();
try {
alterModel(connection, currentModel, desiredModel, continueOnError);
} finally {
returnConnection(connection);
}
}
/**
* {@inheritDoc}
*/
public void alterModel(Database currentModel, Database desiredModel, CreationParameters params,
boolean continueOnError) throws DatabaseOperationException {
Connection connection = borrowConnection();
try {
alterModel(connection, currentModel, desiredModel, params, continueOnError);
} finally {
returnConnection(connection);
}
}
/**
* {@inheritDoc}
*/
public void alterModel(Connection connection, Database currentModel, Database desiredModel, boolean continueOnError)
throws DatabaseOperationException {
String sql = getAlterModelSql(currentModel, desiredModel);
_log.debug("sql");
evaluateBatch(connection, sql, continueOnError);
}
/**
* {@inheritDoc}
*/
public void alterModel(Connection connection, Database currentModel, Database desiredModel,
CreationParameters params, boolean continueOnError) throws DatabaseOperationException {
String sql = getAlterModelSql(currentModel, desiredModel, params);
evaluateBatch(connection, sql, continueOnError);
}
/**
* {@inheritDoc}
*/
public void dropTable(Connection connection, Database model, Table table, boolean continueOnError)
throws DatabaseOperationException {
String sql = getDropTableSql(model, table, continueOnError);
evaluateBatch(connection, sql, continueOnError);
}
/**
* {@inheritDoc}
*/
public void dropTable(Database model, Table table, boolean continueOnError) throws DatabaseOperationException {
Connection connection = borrowConnection();
try {
dropTable(connection, model, table, continueOnError);
} finally {
returnConnection(connection);
}
}
/**
* {@inheritDoc}
*/
public String getDropTableSql(Database model, Table table, boolean continueOnError) {
String sql = null;
try {
StringWriter buffer = new StringWriter();
getSqlBuilder().setWriter(buffer);
getSqlBuilder().dropTable(model, table);
sql = buffer.toString();
} catch (IOException e) {
// won't happen because we're using a string writer
}
return sql;
}
/**
* {@inheritDoc}
*/
public void dropTables(Database model, boolean continueOnError) throws DatabaseOperationException {
dropModel(model, continueOnError);
}
/**
* {@inheritDoc}
*/
public void dropTables(Connection connection, Database model, boolean continueOnError)
throws DatabaseOperationException {
dropModel(connection, model, continueOnError);
}
/**
* {@inheritDoc}
*/
public String getDropTablesSql(Database model, boolean continueOnError) {
return getDropModelSql(model);
}
/**
* {@inheritDoc}
*/
public void dropModel(Database model, boolean continueOnError) throws DatabaseOperationException {
Connection connection = borrowConnection();
try {
dropModel(connection, model, continueOnError);
} finally {
returnConnection(connection);
}
}
/**
* {@inheritDoc}
*/
public void dropModel(Connection connection, Database model, boolean continueOnError)
throws DatabaseOperationException {
String sql = getDropModelSql(model);
evaluateBatch(connection, sql, continueOnError);
}
/**
* {@inheritDoc}
*/
public String getDropModelSql(Database model) {
String sql = null;
try {
StringWriter buffer = new StringWriter();
getSqlBuilder().setWriter(buffer);
getSqlBuilder().dropTables(model);
sql = buffer.toString();
} catch (IOException e) {
// won't happen because we're using a string writer
}
return sql;
}
/**
* Processes the given changes in the specified order. Basically, this method
* finds the appropriate handler method (one of the processChange
* methods) defined in the concrete sql builder for each change, and invokes it.
*
* @param model
* The database model; this object is not going to be changed by this
* method
* @param changes
* The changes
* @param params
* The parameters used in the creation of new tables. Note that for
* existing tables, the parameters won't be applied
* @return The changed database model
*/
protected Database processChanges(Database model, Collection changes, CreationParameters params)
throws IOException, DdlUtilsException {
Database currentModel = new CloneHelper().clone(model);
for (Iterator it = changes.iterator(); it.hasNext();) {
invokeChangeHandler(currentModel, params, it.next());
}
return currentModel;
}
/**
* Invokes the change handler (one of the processChange
methods)
* for the given change object.
*
* @param currentModel
* The current database schema
* @param params
* The parameters used in the creation of new tables. Note that for
* existing tables, the parameters won't be applied
* @param change
* The change object
*/
private void invokeChangeHandler(Database currentModel, CreationParameters params, ModelChange change)
throws IOException {
Class> curClass = getClass();
// find the handler for the change
while ((curClass != null) && !Object.class.equals(curClass)) {
try {
Method method = null;
try {
method = curClass.getDeclaredMethod("processChange",
new Class[] { Database.class, CreationParameters.class, change.getClass() });
} catch (NoSuchMethodException ex) {
// we actually expect this one
}
if (method != null) {
method.invoke(this, new Object[] { currentModel, params, change });
return;
} else {
curClass = curClass.getSuperclass();
}
} catch (InvocationTargetException ex) {
if (ex.getTargetException() instanceof IOException) {
throw (IOException) ex.getTargetException();
} else {
throw new DdlUtilsException(ex.getTargetException());
}
} catch (Exception ex) {
throw new DdlUtilsException(ex);
}
}
throw new DdlUtilsException("No handler for change of type " + change.getClass().getName() + " defined");
}
/**
* Finds the table changed by the change object in the given model.
*
* @param currentModel
* The model to find the table in
* @param change
* The table change
* @return The table
* @throws ModelException
* If the table could not be found
*/
protected Table findChangedTable(Database currentModel, TableChange change) throws ModelException {
Table table = currentModel.findTable(change.getChangedTable(),
getPlatformInfo().isDelimitedIdentifiersSupported());
if (table == null) {
throw new ModelException("Could not find table " + change.getChangedTable() + " in the given model");
} else {
return table;
}
}
/**
* Finds the index changed by the change object in the given model.
*
* @param currentModel
* The model to find the index in
* @param change
* The index change
* @return The index
* @throws ModelException
* If the index could not be found
*/
protected Index findChangedIndex(Database currentModel, IndexChange change) throws ModelException {
Index index = change.findChangedIndex(currentModel, getPlatformInfo().isDelimitedIdentifiersSupported());
if (index == null) {
throw new ModelException(
"Could not find the index to change in table " + change.getChangedTable() + " in the given model");
} else {
return index;
}
}
/**
* Finds the foreign key changed by the change object in the given model.
*
* @param currentModel
* The model to find the foreign key in
* @param change
* The foreign key change
* @return The foreign key
* @throws ModelException
* If the foreign key could not be found
*/
protected ForeignKey findChangedForeignKey(Database currentModel, ForeignKeyChange change) throws ModelException {
ForeignKey fk = change.findChangedForeignKey(currentModel, getPlatformInfo().isDelimitedIdentifiersSupported());
if (fk == null) {
throw new ModelException("Could not find the foreign key to change in table " + change.getChangedTable()
+ " in the given model");
} else {
return fk;
}
}
/**
* Processes a change representing the addition of a table.
*
* @param currentModel
* The current database schema
* @param params
* The parameters used in the creation of new tables. Note that for
* existing tables, the parameters won't be applied
* @param change
* The change object
* @throws IOException
*/
public void processChange(Database currentModel, CreationParameters params, AddTableChange change)
throws IOException {
getSqlBuilder().createTable(currentModel, change.getNewTable(),
params == null ? null : params.getParametersFor(change.getNewTable()));
change.apply(currentModel, isDelimitedIdentifierModeOn());
}
/**
* Processes a change representing the removal of a table.
*
* @param currentModel
* The current database schema
* @param params
* The parameters used in the creation of new tables. Note that for
* existing tables, the parameters won't be applied
* @param change
* The change object
* @throws IOException
* @throws ModelException
*/
public void processChange(Database currentModel, CreationParameters params, RemoveTableChange change)
throws IOException, ModelException {
Table changedTable = findChangedTable(currentModel, change);
getSqlBuilder().dropTable(changedTable);
change.apply(currentModel, isDelimitedIdentifierModeOn());
}
/**
* Processes a change representing the addition of a foreign key.
*
* @param currentModel
* The current database schema
* @param params
* The parameters used in the creation of new tables. Note that for
* existing tables, the parameters won't be applied
* @param change
* The change object
* @throws IOException
*/
public void processChange(Database currentModel, CreationParameters params, AddForeignKeyChange change)
throws IOException {
Table changedTable = findChangedTable(currentModel, change);
getSqlBuilder().createForeignKey(currentModel, changedTable, change.getNewForeignKey());
change.apply(currentModel, isDelimitedIdentifierModeOn());
}
/**
* Processes a change representing the removal of a foreign key.
*
* @param currentModel
* The current database schema
* @param params
* The parameters used in the creation of new tables. Note that for
* existing tables, the parameters won't be applied
* @param change
* The change object
* @throws IOException
* @throws ModelException
*/
public void processChange(Database currentModel, CreationParameters params, RemoveForeignKeyChange change)
throws IOException, ModelException {
Table changedTable = findChangedTable(currentModel, change);
ForeignKey changedFk = findChangedForeignKey(currentModel, change);
getSqlBuilder().dropForeignKey(changedTable, changedFk);
change.apply(currentModel, isDelimitedIdentifierModeOn());
}
/**
* Processes a change representing the addition of an index.
*
* @param currentModel
* The current database schema
* @param params
* The parameters used in the creation of new tables. Note that for
* existing tables, the parameters won't be applied
* @param change
* The change object
* @throws IOException
*/
public void processChange(Database currentModel, CreationParameters params, AddIndexChange change)
throws IOException {
Table changedTable = findChangedTable(currentModel, change);
getSqlBuilder().createIndex(changedTable, change.getNewIndex());
change.apply(currentModel, isDelimitedIdentifierModeOn());
}
/**
* Processes a change representing the removal of an index.
*
* @param currentModel
* The current database schema
* @param params
* The parameters used in the creation of new tables. Note that for
* existing tables, the parameters won't be applied
* @param change
* The change object
* @throws IOException
* @throws ModelException
*/
public void processChange(Database currentModel, CreationParameters params, RemoveIndexChange change)
throws IOException, ModelException {
Table changedTable = findChangedTable(currentModel, change);
Index changedIndex = findChangedIndex(currentModel, change);
getSqlBuilder().dropIndex(changedTable, changedIndex);
change.apply(currentModel, isDelimitedIdentifierModeOn());
}
/**
* Processes a change representing the addition of a column.
*
* @param currentModel
* The current database schema
* @param params
* The parameters used in the creation of new tables. Note that for
* existing tables, the parameters won't be applied
* @param change
* The change object
* @throws IOException
*/
public void processChange(Database currentModel, CreationParameters params, AddColumnChange change)
throws IOException {
Table changedTable = findChangedTable(currentModel, change);
getSqlBuilder().addColumn(currentModel, changedTable, change.getNewColumn());
change.apply(currentModel, isDelimitedIdentifierModeOn());
}
/**
* Processes a change representing the addition of a primary key.
*
* @param currentModel
* The current database schema
* @param params
* The parameters used in the creation of new tables. Note that for
* existing tables, the parameters won't be applied
* @param change
* The change object
* @throws IOException
*/
public void processChange(Database currentModel, CreationParameters params, AddPrimaryKeyChange change)
throws IOException {
Table changedTable = findChangedTable(currentModel, change);
String[] pkColumnNames = change.getPrimaryKeyColumns();
Column[] pkColumns = new Column[pkColumnNames.length];
for (int colIdx = 0; colIdx < pkColumns.length; colIdx++) {
pkColumns[colIdx] = changedTable.findColumn(pkColumnNames[colIdx], isDelimitedIdentifierModeOn());
}
getSqlBuilder().createPrimaryKey(changedTable, pkColumns);
change.apply(currentModel, isDelimitedIdentifierModeOn());
}
/**
* Processes a change representing the recreation of a table.
*
* @param currentModel
* The current database schema
* @param params
* The parameters used in the creation of new tables. Note that for
* existing tables, the parameters won't be applied
* @param change
* The change object
* @throws IOException
*/
public void processChange(Database currentModel, CreationParameters params, RecreateTableChange change)
throws IOException {
// we can only copy the data if no required columns without default value and
// non-autoincrement have been added
boolean canMigrateData = true;
for (Iterator it = change.getOriginalChanges().iterator(); canMigrateData && it.hasNext();) {
TableChange curChange = (TableChange) it.next();
if (curChange instanceof AddColumnChange) {
AddColumnChange addColumnChange = (AddColumnChange) curChange;
if (addColumnChange.getNewColumn().isRequired() && !addColumnChange.getNewColumn().isAutoIncrement()
&& (addColumnChange.getNewColumn().getDefaultValue() == null)) {
_log.warn("Data cannot be retained in table " + change.getChangedTable()
+ " because of the addition of the required column "
+ addColumnChange.getNewColumn().getName());
canMigrateData = false;
}
}
}
Table changedTable = findChangedTable(currentModel, change);
Table targetTable = change.getTargetTable();
Map parameters = (params == null ? null : params.getParametersFor(targetTable));
if (canMigrateData) {
Table tempTable = getTemporaryTableFor(targetTable);
getSqlBuilder().createTemporaryTable(currentModel, tempTable, parameters);
getSqlBuilder().copyData(changedTable, tempTable);
// Note that we don't drop the indices here because the DROP TABLE will take
// care of that
// Likewise, foreign keys have already been dropped as necessary
getSqlBuilder().dropTable(changedTable);
getSqlBuilder().createTable(currentModel, targetTable, parameters);
getSqlBuilder().copyData(tempTable, targetTable);
getSqlBuilder().dropTemporaryTable(currentModel, tempTable);
} else {
getSqlBuilder().dropTable(changedTable);
getSqlBuilder().createTable(currentModel, targetTable, parameters);
}
change.apply(currentModel, isDelimitedIdentifierModeOn());
}
/**
* Creates a temporary table object that corresponds to the given table.
* Database-specific implementations may redefine this method if e.g. the
* database directly supports temporary tables. The default implementation
* simply appends an underscore to the table name and uses that as the table
* name.
*
* @param targetTable
* The target table
* @return The temporary table
*/
protected Table getTemporaryTableFor(Table targetTable) {
CloneHelper cloneHelper = new CloneHelper();
Table table = new Table();
table.setCatalog(targetTable.getCatalog());
table.setSchema(targetTable.getSchema());
table.setName(targetTable.getName() + "_");
table.setType(targetTable.getType());
for (int idx = 0; idx < targetTable.getColumnCount(); idx++) {
// TODO: clone PK status ?
table.addColumn(cloneHelper.clone(targetTable.getColumn(idx), true));
}
return table;
}
/**
* {@inheritDoc}
*/
public Iterator query(Database model, String sql) throws DatabaseOperationException {
return query(model, sql, (Table[]) null);
}
/**
* {@inheritDoc}
*/
public Iterator query(Database model, String sql, Collection
© 2015 - 2024 Weber Informatics LLC | Privacy Policy