TestAgainstLiveDatabaseBase.java :  » Library » dblutils » org » apache » ddlutils » Java Open Source

Java Open Source » Library » dblutils 
dblutils » org » apache » ddlutils » TestAgainstLiveDatabaseBase.java
package org.apache.ddlutils;

/*
 * 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 java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.io.StringWriter;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import javax.sql.DataSource;

import junit.framework.AssertionFailedError;
import junit.framework.TestSuite;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.DynaBean;
import org.apache.commons.beanutils.DynaProperty;
import org.apache.commons.dbcp.BasicDataSource;
import org.apache.commons.logging.LogFactory;
import org.apache.ddlutils.dynabean.SqlDynaBean;
import org.apache.ddlutils.dynabean.SqlDynaClass;
import org.apache.ddlutils.dynabean.SqlDynaProperty;
import org.apache.ddlutils.io.BinaryObjectsHelper;
import org.apache.ddlutils.io.DataReader;
import org.apache.ddlutils.io.DataToDatabaseSink;
import org.apache.ddlutils.io.DatabaseIO;
import org.apache.ddlutils.model.CloneHelper;
import org.apache.ddlutils.model.Column;
import org.apache.ddlutils.model.Database;
import org.apache.ddlutils.model.ForeignKey;
import org.apache.ddlutils.model.Index;
import org.apache.ddlutils.model.IndexColumn;
import org.apache.ddlutils.model.Reference;
import org.apache.ddlutils.model.Table;
import org.apache.ddlutils.model.TypeMap;
import org.apache.ddlutils.platform.CreationParameters;
import org.apache.ddlutils.platform.DefaultValueHelper;
import org.apache.ddlutils.platform.firebird.FirebirdPlatform;
import org.apache.ddlutils.platform.interbase.InterbasePlatform;
import org.apache.ddlutils.util.StringUtilsExt;

/**
 * Base class tests that are executed against a live database.
 * 
 * @version $Revision: 289996 $
 */
public abstract class TestAgainstLiveDatabaseBase extends TestPlatformBase
{
    /** The name of the property that specifies properties file with the settings for the connection to test against. */
    public static final String JDBC_PROPERTIES_PROPERTY = "jdbc.properties.file";
    /** The prefix for properties of the datasource. */
    public static final String DATASOURCE_PROPERTY_PREFIX = "datasource.";
    /** The prefix for properties for ddlutils. */
    public static final String DDLUTILS_PROPERTY_PREFIX = "ddlutils.";
    /** The property for specifying the platform. */
    public static final String DDLUTILS_PLATFORM_PROPERTY = DDLUTILS_PROPERTY_PREFIX + "platform";
    /** The property specifying the catalog for the tests. */
    public static final String DDLUTILS_CATALOG_PROPERTY = DDLUTILS_PROPERTY_PREFIX + "catalog";
    /** The property specifying the schema for the tests. */
    public static final String DDLUTILS_SCHEMA_PROPERTY = DDLUTILS_PROPERTY_PREFIX + "schema";
    /** The prefix for table creation properties. */
    public static final String DDLUTILS_TABLE_CREATION_PREFIX = DDLUTILS_PROPERTY_PREFIX + "tableCreation.";

    /**
     * Creates the test suite for the given test class which must be a sub class of
     * {@link RoundtripTestBase}. If the platform supports it, it will be tested
     * with both delimited and undelimited identifiers.
     * 
     * @param testedClass The tested class
     * @return The tests
     */
    protected static TestSuite getTests(Class testedClass)
    {
        if (!TestAgainstLiveDatabaseBase.class.isAssignableFrom(testedClass) ||
            Modifier.isAbstract(testedClass.getModifiers()))
        {
            throw new DdlUtilsException("Cannot create parameterized tests for class "+testedClass.getName());
        }

        TestSuite  suite      = new TestSuite();
        Properties props      = readTestProperties();

        if (props == null)
        {
            return suite;
        }

        DataSource dataSource   = initDataSourceFromProperties(props);
        String     databaseName = determineDatabaseName(props, dataSource);

        try
        {
            Method[]               methods = testedClass.getMethods();
            PlatformInfo           info    = null;
            TestAgainstLiveDatabaseBase newTest;
    
            for (int idx = 0; (methods != null) && (idx < methods.length); idx++)
            {
                if (methods[idx].getName().startsWith("test") &&
                    ((methods[idx].getParameterTypes() == null) || (methods[idx].getParameterTypes().length == 0)))
                {
                    newTest = (TestAgainstLiveDatabaseBase)testedClass.newInstance();
                    newTest.setName(methods[idx].getName());
                    newTest.setTestProperties(props);
                    newTest.setDataSource(dataSource);
                    newTest.setDatabaseName(databaseName);
                    newTest.setUseDelimitedIdentifiers(false);
                    suite.addTest(newTest);

                    if (info == null)
                    {
                        info = PlatformFactory.createNewPlatformInstance(newTest.getDatabaseName()).getPlatformInfo();
                    }
                    if (info.isDelimitedIdentifiersSupported())
                    {
                        newTest = (TestAgainstLiveDatabaseBase)testedClass.newInstance();
                        newTest.setName(methods[idx].getName());
                        newTest.setTestProperties(props);
                        newTest.setDataSource(dataSource);
                        newTest.setDatabaseName(databaseName);
                        newTest.setUseDelimitedIdentifiers(true);
                        suite.addTest(newTest);
                    }
                }
            }
        }
        catch (Exception ex)
        {
            throw new DdlUtilsException(ex);
        }
        
        return suite;
    }

    /**
     * Reads the test properties as specified by the property.
     * 
     * @return The properties or <code>null</code> if no properties have been specified
     */
    protected static Properties readTestProperties()
    {
        String propFile = System.getProperty(JDBC_PROPERTIES_PROPERTY);

        if (propFile == null)
        {
            return null;
        }

        InputStream propStream = null;

        try
        {
            propStream = TestAgainstLiveDatabaseBase.class.getResourceAsStream(propFile);

            if (propStream == null)
            {
                propStream = new FileInputStream(propFile);
            }

            Properties props = new Properties();

            props.load(propStream);
            return props;
        }
        catch (Exception ex)
        {
            throw new RuntimeException(ex);
        }
        finally
        {
            if (propStream != null)
            {
                try
                {
                    propStream.close();
                }
                catch (IOException ex)
                {
                    LogFactory.getLog(TestAgainstLiveDatabaseBase.class).error("Could not close the stream used to read the test jdbc properties", ex);
                }
            }
        }
    }

    /**
     * Initializes the test datasource and the platform.
     * 
     * @param props The properties to initialize from
     * @return The data source object
     */
    private static DataSource initDataSourceFromProperties(Properties props)
    {
        if (props == null)
        {
            return null;
        }

        try
        {
            String     dataSourceClass = props.getProperty(DATASOURCE_PROPERTY_PREFIX + "class", BasicDataSource.class.getName());
            DataSource dataSource      = (DataSource)Class.forName(dataSourceClass).newInstance();

            for (Iterator it = props.entrySet().iterator(); it.hasNext();)
            {
                Map.Entry entry    = (Map.Entry)it.next();
                String    propName = (String)entry.getKey();

                if (propName.startsWith(DATASOURCE_PROPERTY_PREFIX) && !propName.equals(DATASOURCE_PROPERTY_PREFIX +"class"))
                {
                    BeanUtils.setProperty(dataSource,
                                          propName.substring(DATASOURCE_PROPERTY_PREFIX.length()),
                                          entry.getValue());
                }
            }
            return dataSource;
        }
        catch (Exception ex)
        {
            throw new DatabaseOperationException(ex);
        }
    }

    /**
     * Determines the name of the platform to use from the properties or the data source if no
     * platform is specified in the properties.
     * 
     * @param props      The test properties
     * @param dataSource The data source
     * @return The name of the platform
     */
    private static String determineDatabaseName(Properties props, DataSource dataSource)
    {
        String platformName = props.getProperty(DDLUTILS_PLATFORM_PROPERTY);

        if (platformName == null)
        {
            // property not set, then try to determine
            platformName = new PlatformUtils().determineDatabaseType(dataSource);
            if (platformName == null)
            {
                throw new DatabaseOperationException("Could not determine platform from datasource, please specify it in the jdbc.properties via the ddlutils.platform property");
            }
        }
        return platformName;
    }


    /** The test properties as defined by an external properties file. */
    private Properties _testProps;
    /** The data source to test against. */
    private DataSource _dataSource;
    /** The database name. */
    private String _databaseName;
    /** The database model. */
    private Database _model;
    /** Whether to use delimited identifiers for the test. */
    private boolean _useDelimitedIdentifiers;

    /**
     * Returns the test properties.
     * 
     * @return The properties
     */
    protected Properties getTestProperties()
    {
        return _testProps;
    }

    /**
     * Sets the test properties.
     * 
     * @param props The properties
     */
    private void setTestProperties(Properties props)
    {
        _testProps = props;
    }

    /**
     * Returns the test table creation parameters for the given model.
     * 
     * @param model The model
     * @return The creation parameters
     */
    protected CreationParameters getTableCreationParameters(Database model)
    {
        CreationParameters params = new CreationParameters();

        for (Iterator entryIt = _testProps.entrySet().iterator(); entryIt.hasNext();)
        {
            Map.Entry entry = (Map.Entry)entryIt.next();
            String    name  = (String)entry.getKey();
            String    value = (String)entry.getValue();

            if (name.startsWith(DDLUTILS_TABLE_CREATION_PREFIX))
            {
                name = name.substring(DDLUTILS_TABLE_CREATION_PREFIX.length());
                for (int tableIdx = 0; tableIdx < model.getTableCount(); tableIdx++)
                {
                    params.addParameter(model.getTable(tableIdx), name, value);
                }
            }
        }
        return params;
    }

    /**
     * Specifies whether the test shall use delimited identifiers.
     * 
     * @param useDelimitedIdentifiers Whether to use delimited identifiers
     */
    protected void setUseDelimitedIdentifiers(boolean useDelimitedIdentifiers)
    {
        _useDelimitedIdentifiers = useDelimitedIdentifiers;
    }

    /**
     * Determines whether the test shall use delimited identifiers.
     * 
     * @return Whether to use delimited identifiers
     */
    protected boolean isUseDelimitedIdentifiers()
    {
        return _useDelimitedIdentifiers;
    }

    /**
     * Returns the data source.
     * 
     * @return The data source
     */
    protected DataSource getDataSource()
    {
        return _dataSource;
    }

    /**
     * Sets the data source.
     * 
     * @param dataSource The data source
     */
    private void setDataSource(DataSource dataSource)
    {
        _dataSource = dataSource;
    }

    /**
     * {@inheritDoc}
     */
    protected String getDatabaseName()
    {
        return _databaseName;
    }

    /**
     * Sets the database name.
     * 
     * @param databaseName The name of the database
     */
    private void setDatabaseName(String databaseName)
    {
        _databaseName = databaseName;
    }

    /**
     * Returns the database model.
     * 
     * @return The model
     */
    protected Database getModel()
    {
        return _model;
    }

    /**
     * {@inheritDoc}
     */
    protected void setUp() throws Exception
    {
        super.setUp();
        getPlatform().setDataSource(getDataSource());
        getPlatform().setDelimitedIdentifierModeOn(_useDelimitedIdentifiers);
    }

    /**
     * {@inheritDoc}
     */
    protected void tearDown() throws Exception
    {
        try
        {
            if (_model != null)
            {
                dropDatabase();
                _model = null;
            }
        }
        finally
        {
            assertAndEnsureClearDatabase();
            super.tearDown();
        }
    }

    /**
     * Creates a new database from the given XML database schema.
     * 
     * @param schemaXml The XML database schema
     * @return The parsed database model
     */
    protected Database createDatabase(String schemaXml) throws DatabaseOperationException
    {
        Database model = parseDatabaseFromString(schemaXml);

        createDatabase(model);
        return model;
    }

    /**
     * Creates a new database from the given model.
     * 
     * @param model The model
     */
    protected void createDatabase(Database model) throws DatabaseOperationException
    {
        try
        {
            _model = model;

            getPlatform().setSqlCommentsOn(false);
            getPlatform().createModel(_model, getTableCreationParameters(_model), false, false);
        }
        catch (Exception ex)
        {
            throw new DatabaseOperationException(ex);
        }
    }

    /**
     * Alters the database to match the given model.
     * 
     * @param schemaXml The model XML
     * @return The model object
     */
    protected Database alterDatabase(String schemaXml) throws DatabaseOperationException
    {
        Database model = parseDatabaseFromString(schemaXml);

        alterDatabase(model);
        return model;
    }

    /**
     * Alters the database to match the given model.
     * 
     * @param desiredModel The model
     */
    protected void alterDatabase(Database desiredModel) throws DatabaseOperationException
    {
        try
        {
            _model = desiredModel;
            _model.resetDynaClassCache();

            Database liveModel = readModelFromDatabase(desiredModel.getName());

            getPlatform().setSqlCommentsOn(false);
            getPlatform().alterModel(liveModel, _model, getTableCreationParameters(_model), false);
        }
        catch (Exception ex)
        {
            throw new DatabaseOperationException(ex);
        }
    }

    /**
     * Inserts data into the database.
     * 
     * @param dataXml The data xml
     * @return The database
     */
    protected Database insertData(String dataXml) throws DatabaseOperationException
    {
        try
        {
            DataReader dataReader = new DataReader();

            dataReader.setModel(_model);
            dataReader.setSink(new DataToDatabaseSink(getPlatform(), _model));
            dataReader.getSink().start();
            dataReader.read(new StringReader(dataXml));
            dataReader.getSink().end();
            return _model;
        }
        catch (Exception ex)
        {
            throw new DatabaseOperationException(ex);
        }
    }

    /**
     * Drops the tables defined in the database model.
     */
    protected void dropDatabase() throws DatabaseOperationException
    {
        getPlatform().dropModel(_model, true);
    }

    /**
     * Inserts a row into the designated table.
     * 
     * @param tableName    The name of the table (case insensitive)
     * @param columnValues The values for the columns in order of definition
     * @return The dyna bean for the row
     */
    protected DynaBean insertRow(String tableName, Object[] columnValues)
    {
        Table    table = getModel().findTable(tableName);
        DynaBean bean  = getModel().createDynaBeanFor(table);

        for (int idx = 0; (idx < table.getColumnCount()) && (idx < columnValues.length); idx++)
        {
            Column column = table.getColumn(idx);

            bean.set(column.getName(), columnValues[idx]);
        }
        getPlatform().insert(getModel(), bean);
        return bean;
    }

    /**
     * Updates the row in the designated table.
     * 
     * @param tableName    The name of the table (case insensitive)
     * @param oldBean      The bean representing the current row
     * @param columnValues The values for the columns in order of definition
     * @return The dyna bean for the new row
     */
    protected DynaBean updateRow(String tableName, DynaBean oldBean, Object[] columnValues)
    {
        Table    table = getModel().findTable(tableName);
        DynaBean bean  = getModel().createDynaBeanFor(table);

        for (int idx = 0; (idx < table.getColumnCount()) && (idx < columnValues.length); idx++)
        {
            Column column = table.getColumn(idx);

            bean.set(column.getName(), columnValues[idx]);
        }
        getPlatform().update(getModel(), oldBean, bean);
        return bean;
    }

    /**
     * Deletes the specified row from the table.
     * 
     * @param tableName      The name of the table (case insensitive)
     * @param pkColumnValues The values for the pk columns in order of definition
     */
    protected void deleteRow(String tableName, Object[] pkColumnValues)
    {
        Table    table     = getModel().findTable(tableName);
        DynaBean bean      = getModel().createDynaBeanFor(table);
        Column[] pkColumns = table.getPrimaryKeyColumns();

        for (int idx = 0; (idx < pkColumns.length) && (idx < pkColumnValues.length); idx++)
        {
            bean.set(pkColumns[idx].getName(), pkColumnValues[idx]);
        }
        getPlatform().delete(getModel(), bean);
    }

    /**
     * Returns a "SELECT * FROM [table name]" statement. It also takes
     * delimited identifier mode into account if enabled.
     *  
     * @param table       The table
     * @param orderColumn The column to order the rows by (can be <code>null</code>)
     * @return The statement
     */
    protected String getSelectQueryForAllString(Table table, String orderColumn)
    {
        StringBuffer query = new StringBuffer();

        query.append("SELECT * FROM ");
        if (getPlatform().isDelimitedIdentifierModeOn())
        {
            query.append(getPlatformInfo().getDelimiterToken());
        }
        query.append(table.getName());
        if (getPlatform().isDelimitedIdentifierModeOn())
        {
            query.append(getPlatformInfo().getDelimiterToken());
        }
        if (orderColumn != null)
        {
            query.append(" ORDER BY ");
            if (getPlatform().isDelimitedIdentifierModeOn())
            {
                query.append(getPlatformInfo().getDelimiterToken());
            }
            query.append(orderColumn);
            if (getPlatform().isDelimitedIdentifierModeOn())
            {
                query.append(getPlatformInfo().getDelimiterToken());
            }
        }
        return query.toString();
    }

    /**
     * Retrieves all rows from the given table.
     * 
     * @param tableName The table
     * @return The rows
     */
    protected List getRows(String tableName)
    {
        Table table = getModel().findTable(tableName, getPlatform().isDelimitedIdentifierModeOn());
        
        return getPlatform().fetch(getModel(),
                                   getSelectQueryForAllString(table, null),
                                   new Table[] { table });
    }

    /**
     * Retrieves all rows from the given table.
     * 
     * @param tableName   The table
     * @param orderColumn The column to order the rows by
     * @return The rows
     */
    protected List getRows(String tableName, String orderColumn)
    {
        Table table = getModel().findTable(tableName, getPlatform().isDelimitedIdentifierModeOn());
        
        return getPlatform().fetch(getModel(),
                                   getSelectQueryForAllString(table, orderColumn),
                                   new Table[] { table });
    }

    /**
     * Checks that the database is clear, and if not clears it (no tables, sequences etc. left) and
     * throws an {@link AssertionFailedError}.
     */
    protected void assertAndEnsureClearDatabase()
    {
        Database liveModel = readModelFromDatabase("tmp");
        boolean  hasStuff  = false;

        if (liveModel.getTableCount() > 0)
        {
            hasStuff = true;
            try
            {
                getPlatform().dropModel(liveModel, true);
            }
            catch (Exception ex)
            {
                getLog().error("Could not clear database", ex);
            }
        }
        if (FirebirdPlatform.DATABASENAME.equals(getPlatform().getName()) ||
            InterbasePlatform.DATABASENAME.equals(getPlatform().getName()))
        {
            Connection connection = null;

            try
            {
                connection = getPlatform().borrowConnection();

                hasStuff = hasStuff | dropTriggers(connection);
                hasStuff = hasStuff | dropGenerators(connection);
            }
            catch (Exception ex)
            {
                getLog().error("Could not clear database", ex);
            }
            finally
            {
                getPlatform().returnConnection(connection);
            }
        }
        // TODO: Check for sequences
        if (hasStuff)
        {
            fail("Database is not empty after test");
        }
    }

    /**
     * Drops generators left by a test in a Firebird/Interbase database.
     * 
     * @param connection The database connection
     * @return Whether generators were dropped
     */
    private boolean dropGenerators(Connection connection)
    {
        Statement stmt          = null;
        boolean   hasGenerators = false;

        try
        {
            stmt = connection.createStatement();

            ResultSet rs    = stmt.executeQuery("SELECT RDB$GENERATOR_NAME FROM RDB$GENERATORS WHERE RDB$GENERATOR_NAME NOT LIKE '%$%'");
            List      names = new ArrayList();
    
            while (rs.next())
            {
                names.add(rs.getString(1));
            }
            rs.close();
    
            for (Iterator it = names.iterator(); it.hasNext();)
            {
                String name = (String)it.next();

                if (name.toLowerCase().startsWith("gen_"))
                {
                    hasGenerators = true;
                    stmt.execute("DROP GENERATOR " + name);
                }
            }
        }
        catch (Exception ex)
        {
            getLog().error("Error while dropping the remaining generators", ex);
        }
        finally
        {
            if (stmt != null)
            {
                try
                {
                    stmt.close();
                }
                catch (Exception ex)
                {
                    getLog().error("Error while clearing the database", ex);
                }
            }
        }
        return hasGenerators;
    }

    /**
     * Drops triggers left by a test in a Firebird/Interbase database.
     * 
     * @param connection The database connection
     * @return Whether triggers were dropped
     */
    private boolean dropTriggers(Connection connection)
    {
        Statement stmt        = null;
        boolean   hasTriggers = false;

        try
        {
            stmt = connection.createStatement();

            ResultSet rs    = stmt.executeQuery("SELECT * FROM RDB$TRIGGERS WHERE RDB$SYSTEM_FLAG IS NULL OR RDB$SYSTEM_FLAG = 0");
            List      names = new ArrayList();
    
            while (rs.next())
            {
                names.add(rs.getString(1));
            }
            rs.close();
    
            for (Iterator it = names.iterator(); it.hasNext();)
            {
                String name = (String)it.next();

                if (name.toLowerCase().startsWith("trg_"))
                {
                    hasTriggers = true;
                    stmt.execute("DROP TRIGGER " + name);
                }
            }
        }
        catch (Exception ex)
        {
            getLog().error("Error while dropping the remaining triggers", ex);
        }
        finally
        {
            if (stmt != null)
            {
                try
                {
                    stmt.close();
                }
                catch (Exception ex)
                {
                    getLog().error("Error while clearing the database", ex);
                }
            }
        }
        return hasTriggers;
    }
    
    /**
     * Reads the database model from a live database.
     * 
     * @param databaseName The name of the resulting database
     * @return The model
     */
    protected Database readModelFromDatabase(String databaseName)
    {
      Properties props   = getTestProperties();
        String     catalog = props.getProperty(DDLUTILS_CATALOG_PROPERTY);
        String     schema  = props.getProperty(DDLUTILS_SCHEMA_PROPERTY);

      return getPlatform().readModelFromDatabase(databaseName, catalog, schema, null);
    }

    /**
     * Returns a copy of the given model adjusted for type changes because of the native type mappings
     * which when read back from the database will map to different types.
     * 
     * @param sourceModel The source model
     * @return The adjusted model
     */
    protected Database adjustModel(Database sourceModel)
    {
        Database model = new CloneHelper().clone(sourceModel);

        for (int tableIdx = 0; tableIdx < model.getTableCount(); tableIdx++)
        {
            Table table = model.getTable(tableIdx);

            for (int columnIdx = 0; columnIdx < table.getColumnCount(); columnIdx++)
            {
                Column column     = table.getColumn(columnIdx);
                int    origType   = column.getTypeCode();
                int    targetType = getPlatformInfo().getTargetJdbcType(origType);

                // we adjust the column types if the native type would back-map to a
                // different jdbc type
                if (targetType != origType)
                {
                    column.setTypeCode(targetType);
                    // we should also adapt the default value
                    if (column.getDefaultValue() != null)
                    {
                        DefaultValueHelper helper = getPlatform().getSqlBuilder().getDefaultValueHelper();

                        column.setDefaultValue(helper.convert(column.getDefaultValue(), origType, targetType));
                    }
                }
                // we also promote the default size if the column has no size
                // spec of its own
                if ((column.getSize() == null) && getPlatformInfo().hasSize(targetType))
                {
                    Integer defaultSize = getPlatformInfo().getDefaultSize(targetType);

                    if (defaultSize != null)
                    {
                        column.setSize(defaultSize.toString());
                    }
                }
                // finally the platform might return a synthetic default value if the column
                // is a primary key column
                if (getPlatformInfo().isSyntheticDefaultValueForRequiredReturned() &&
                    (column.getDefaultValue() == null) && column.isRequired() && !column.isAutoIncrement())
                {
                    switch (column.getTypeCode())
                    {
                        case Types.TINYINT:
                        case Types.SMALLINT:
                        case Types.INTEGER:
                        case Types.BIGINT:
                            column.setDefaultValue("0");
                            break;
                        case Types.REAL:
                        case Types.FLOAT:
                        case Types.DOUBLE:
                            column.setDefaultValue("0.0");
                            break;
                        case Types.BIT:
                            column.setDefaultValue("false");
                            break;
                        default:
                            column.setDefaultValue("");
                            break;
                    }
                }
                if (column.isPrimaryKey() && getPlatformInfo().isPrimaryKeyColumnAutomaticallyRequired())
                {
                    column.setRequired(true);
                }
                if (column.isAutoIncrement() && getPlatformInfo().isIdentityColumnAutomaticallyRequired())
                {
                    column.setRequired(true);
                }
            }
            // we also add the default names to foreign keys that are initially unnamed
            for (int fkIdx = 0; fkIdx < table.getForeignKeyCount(); fkIdx++)
            {
                ForeignKey fk = table.getForeignKey(fkIdx);

                if (fk.getName() == null)
                {
                    fk.setName(getPlatform().getSqlBuilder().getForeignKeyName(table, fk));
                }
            }
        }
        return model;
    }

    /**
     * Returns the original model adjusted for type changes because of the native type mappings
     * which when read back from the database will map to different types.
     * 
     * @return The adjusted model
     */
    protected Database getAdjustedModel()
    {
        Database model = getModel();

        return model == null ? null : adjustModel(model);
    }

    /**
     * Returns the SQL for altering the live database so that it matches the given model.
     * 
     * @param desiredModel The desired model
     * @return The alteration SQL
     */
    protected String getAlterTablesSql(Database desiredModel)
    {
        Database liveModel = readModelFromDatabase(desiredModel.getName());

        return getPlatform().getAlterModelSql(liveModel, desiredModel, getTableCreationParameters(desiredModel));
    }

    /**
     * Determines the value of the bean's property that has the given name. Depending on the
     * case-setting of the current builder, the case of teh name is considered or not. 
     * 
     * @param bean     The bean
     * @param propName The name of the property
     * @return The value
     */
    protected Object getPropertyValue(DynaBean bean, String propName)
    {
        if (getPlatform().isDelimitedIdentifierModeOn())
        {
            return bean.get(propName);
        }
        else
        {
            DynaProperty[] props = bean.getDynaClass().getDynaProperties();
    
            for (int idx = 0; idx < props.length; idx++)
            {
                if (propName.equalsIgnoreCase(props[idx].getName()))
                {
                    return bean.get(props[idx].getName());
                }
            }
            throw new IllegalArgumentException("The bean has no property with the name "+propName);
        }
    }

    /**
     * Asserts that the two given database models are equal, and if not, writes both of them
     * in XML form to <code>stderr</code>.
     * 
     * @param expected The expected model
     * @param actual   The actual model
     */
    protected void assertEquals(Database expected, Database actual)
    {
        assertEquals(expected, actual, _useDelimitedIdentifiers);
    }

    /**
     * Asserts that the two given database tables are equal.
     * 
     * @param expected The expected table
     * @param actual   The actual table
     */
    protected void assertEquals(Table expected, Table actual)
    {
        assertEquals(expected, actual, _useDelimitedIdentifiers);
    }

    /**
     * Asserts that the two given columns are equal.
     * 
     * @param expected The expected column
     * @param actual   The actual column
     */
    protected void assertEquals(Column expected, Column actual)
    {
        assertEquals(expected, actual, _useDelimitedIdentifiers);
    }

    /**
     * Asserts that the two given foreign keys are equal.
     * 
     * @param expected The expected foreign key
     * @param actual   The actual foreign key
     */
    protected void assertEquals(ForeignKey expected, ForeignKey actual)
    {
        assertEquals(expected, actual, _useDelimitedIdentifiers);
    }

    /**
     * Asserts that the two given references are equal.
     * 
     * @param expected The expected reference
     * @param actual   The actual reference
     */
    protected void assertEquals(Reference expected, Reference actual)
    {
        assertEquals(expected, actual, _useDelimitedIdentifiers);
    }

    /**
     * Asserts that the two given indices are equal.
     * 
     * @param expected The expected index
     * @param actual   The actual index
     */
    protected void assertEquals(Index expected, Index actual)
    {
        assertEquals(expected, actual, _useDelimitedIdentifiers);
    }

    /**
     * Asserts that the two given index columns are equal.
     * 
     * @param expected The expected index column
     * @param actual   The actual index column
     */
    protected void assertEquals(IndexColumn expected, IndexColumn actual)
    {
        assertEquals(expected, actual, _useDelimitedIdentifiers);
    }

    /**
     * Compares the specified attribute value of the given bean with the expected object.
     * 
     * @param expected The expected object
     * @param bean     The bean
     * @param attrName The attribute name
     */
    protected void assertEquals(Object expected, Object bean, String attrName)
    {
        DynaBean dynaBean = (DynaBean)bean;
        Object   value    = dynaBean.get(attrName);

        if ((value instanceof byte[]) && !(expected instanceof byte[]) && (dynaBean instanceof SqlDynaBean))
        {
            SqlDynaClass dynaClass = (SqlDynaClass)((SqlDynaBean)dynaBean).getDynaClass();
            Column       column    = ((SqlDynaProperty)dynaClass.getDynaProperty(attrName)).getColumn();

            if (TypeMap.isBinaryType(column.getTypeCode()))
            {
                value = new BinaryObjectsHelper().deserialize((byte[])value);
            }
        }
        if (expected == null)
        {
            assertNull(value);
        }
        else
        {
            assertEquals(expected, value);
        }
    }

    /**
     * Asserts that the two given database models are equal, and if not, writes both of them
     * in XML form to <code>stderr</code>.
     * 
     * @param expected      The expected model
     * @param actual        The actual model
     * @param caseSensitive Whether case matters when comparing
     */
    protected void assertEquals(Database expected, Database actual, boolean caseSensitive)
    {
        try
        {
            assertEquals("Model names do not match.",
                         expected.getName(),
                         actual.getName());
            assertEquals("Not the same number of tables.",
                         expected.getTableCount(),
                         actual.getTableCount());
            for (int tableIdx = 0; tableIdx < actual.getTableCount(); tableIdx++)
            {
                assertEquals(expected.getTable(tableIdx),
                             actual.getTable(tableIdx),
                             caseSensitive);
            }
        }
        catch (Throwable ex)
        {
            StringWriter writer = new StringWriter();
            DatabaseIO   dbIo   = new DatabaseIO();

            dbIo.write(expected, writer);

            getLog().error("Expected model:\n" + writer.toString());

            writer = new StringWriter();
            dbIo.write(actual, writer);

            getLog().error("Actual model:\n" + writer.toString());

            if (ex instanceof Error)
            {
                throw (Error)ex;
            }
            else
            {
                throw new DdlUtilsException(ex);
            }
        }
    }

    /**
     * Asserts that the two given database tables are equal.
     * 
     * @param expected      The expected table
     * @param actual        The actual table
     * @param caseSensitive Whether case matters when comparing
     */
    protected void assertEquals(Table expected, Table actual, boolean caseSensitive)
    {
        if (caseSensitive)
        {
            assertEquals("Table names do not match.",
                         getPlatform().getSqlBuilder().shortenName(expected.getName(), getSqlBuilder().getMaxTableNameLength()),
                         getPlatform().getSqlBuilder().shortenName(actual.getName(), getSqlBuilder().getMaxTableNameLength()));
        }
        else
        {
            assertEquals("Table names do not match (ignoring case).",
                         getPlatform().getSqlBuilder().shortenName(expected.getName().toUpperCase(), getSqlBuilder().getMaxTableNameLength()),
                         getPlatform().getSqlBuilder().shortenName(actual.getName().toUpperCase(), getSqlBuilder().getMaxTableNameLength()));
        }
        assertEquals("Not the same number of columns in table "+actual.getName()+".",
                     expected.getColumnCount(),
                     actual.getColumnCount());
        for (int columnIdx = 0; columnIdx < actual.getColumnCount(); columnIdx++)
        {
            assertEquals(expected.getColumn(columnIdx),
                         actual.getColumn(columnIdx),
                         caseSensitive);
        }
        assertEquals("Not the same number of foreign keys in table "+actual.getName()+".",
                     expected.getForeignKeyCount(),
                     actual.getForeignKeyCount());
        // order is not assumed with the way foreignkeys are returned.
        for (int expectedFkIdx = 0; expectedFkIdx < expected.getForeignKeyCount(); expectedFkIdx++)
        {
            ForeignKey expectedFk   = expected.getForeignKey(expectedFkIdx);
            String     expectedName = getPlatform().getSqlBuilder().shortenName(expectedFk.getName(), getSqlBuilder().getMaxForeignKeyNameLength());

            for (int actualFkIdx = 0; actualFkIdx < actual.getForeignKeyCount(); actualFkIdx++)
            {
                ForeignKey actualFk   = actual.getForeignKey(actualFkIdx);
                String     actualName = getPlatform().getSqlBuilder().shortenName(actualFk.getName(), getSqlBuilder().getMaxForeignKeyNameLength());

                if (StringUtilsExt.equals(expectedName, actualName, caseSensitive))
                {
                    assertEquals(expectedFk,
                                 actualFk,
                                 caseSensitive);
                }
            }
        }
        assertEquals("Not the same number of indices in table "+actual.getName()+".",
                     expected.getIndexCount(),
                     actual.getIndexCount());
        for (int indexIdx = 0; indexIdx < actual.getIndexCount(); indexIdx++)
        {
            assertEquals(expected.getIndex(indexIdx),
                         actual.getIndex(indexIdx),
                         caseSensitive);
        }
    }

    /**
     * Asserts that the two given columns are equal.
     * 
     * @param expected      The expected column
     * @param actual        The actual column
     * @param caseSensitive Whether case matters when comparing
     */
    protected void assertEquals(Column expected, Column actual, boolean caseSensitive)
    {
        if (caseSensitive)
        {
            assertEquals("Column names do not match.",
                         getPlatform().getSqlBuilder().shortenName(expected.getName(), getSqlBuilder().getMaxColumnNameLength()),
                         getPlatform().getSqlBuilder().shortenName(actual.getName(), getSqlBuilder().getMaxColumnNameLength()));
        }
        else
        {
            assertEquals("Column names do not match (ignoring case).",
                         getPlatform().getSqlBuilder().shortenName(expected.getName().toUpperCase(), getSqlBuilder().getMaxColumnNameLength()),
                         getPlatform().getSqlBuilder().shortenName(actual.getName().toUpperCase(), getSqlBuilder().getMaxColumnNameLength()));
        }
        assertEquals("Primary key status not the same for column "+actual.getName()+".",
                     expected.isPrimaryKey(),
                     actual.isPrimaryKey());
        assertEquals("Required status not the same for column "+actual.getName()+".",
                     expected.isRequired(),
                     actual.isRequired());
        if (getPlatformInfo().getIdentityStatusReadingSupported())
        {
            // we're only comparing this if the platform can actually read the
            // auto-increment status back from an existing database
            assertEquals("Auto-increment status not the same for column "+actual.getName()+".",
                         expected.isAutoIncrement(),
                         actual.isAutoIncrement());
        }
        assertEquals("Type not the same for column "+actual.getName()+".",
                     expected.getType(),
                     actual.getType());
        assertEquals("Type code not the same for column "+actual.getName()+".",
                     expected.getTypeCode(),
                     actual.getTypeCode());
        assertEquals("Parsed default values do not match for column "+actual.getName()+".",
                     expected.getParsedDefaultValue(),
                     actual.getParsedDefaultValue());

        // comparing the size makes only sense for types where it is relevant
        if ((expected.getTypeCode() == Types.NUMERIC) ||
            (expected.getTypeCode() == Types.DECIMAL))
        {
            assertEquals("Precision not the same for column "+actual.getName()+".",
                         expected.getSizeAsInt(),
                         actual.getSizeAsInt());
            assertEquals("Scale not the same for column "+actual.getName()+".",
                         expected.getScale(),
                         actual.getScale());
        }
        else if ((expected.getTypeCode() == Types.CHAR) ||
                 (expected.getTypeCode() == Types.VARCHAR) ||
                 (expected.getTypeCode() == Types.BINARY) ||
                 (expected.getTypeCode() == Types.VARBINARY))
        {
            assertEquals("Size not the same for column "+actual.getName()+".",
                         expected.getSize(),
                         actual.getSize());
        }
    }

    /**
     * Asserts that the two given foreign keys are equal.
     * 
     * @param expected      The expected foreign key
     * @param actual        The actual foreign key
     * @param caseSensitive Whether case matters when comparing
     */
    protected void assertEquals(ForeignKey expected, ForeignKey actual, boolean caseSensitive)
    {
        if (caseSensitive)
        {
            assertEquals("Foreign key names do not match.",
                         getPlatform().getSqlBuilder().shortenName(expected.getName(), getSqlBuilder().getMaxForeignKeyNameLength()),
                         getPlatform().getSqlBuilder().shortenName(actual.getName(), getSqlBuilder().getMaxForeignKeyNameLength()));
            assertEquals("Referenced table names do not match.",
                         getPlatform().getSqlBuilder().shortenName(expected.getForeignTableName(), getSqlBuilder().getMaxTableNameLength()),
                         getPlatform().getSqlBuilder().shortenName(actual.getForeignTableName(), getSqlBuilder().getMaxTableNameLength()));
        }
        else
        {
            assertEquals("Foreign key names do not match (ignoring case).",
                         getPlatform().getSqlBuilder().shortenName(expected.getName().toUpperCase(), getSqlBuilder().getMaxForeignKeyNameLength()),
                         getPlatform().getSqlBuilder().shortenName(actual.getName().toUpperCase(), getSqlBuilder().getMaxForeignKeyNameLength()));
            assertEquals("Referenced table names do not match (ignoring case).",
                         getPlatform().getSqlBuilder().shortenName(expected.getForeignTableName().toUpperCase(), getSqlBuilder().getMaxTableNameLength()),
                         getPlatform().getSqlBuilder().shortenName(actual.getForeignTableName().toUpperCase(), getSqlBuilder().getMaxTableNameLength()));
        }

        assertTrue("Not the same onUpdate setting in foreign key "+actual.getName()+": expected = "+expected.getOnUpdate()+", actual = "+actual.getOnUpdate(),
                   expected.getOnUpdate().equals(actual.getOnUpdate()) ||
                   getPlatformInfo().areEquivalentOnUpdateActions(expected.getOnUpdate(), actual.getOnUpdate()));
        assertTrue("Not the same onDelete setting in foreign key "+actual.getName()+": expected = "+expected.getOnDelete()+", actual = "+actual.getOnDelete(),
                   expected.getOnDelete().equals(actual.getOnDelete()) ||
                   getPlatformInfo().areEquivalentOnDeleteActions(expected.getOnDelete(), actual.getOnDelete()));

        assertEquals("Not the same number of references in foreign key "+actual.getName()+".",
                     expected.getReferenceCount(),
                     actual.getReferenceCount());
        for (int refIdx = 0; refIdx < actual.getReferenceCount(); refIdx++)
        {
            assertEquals(expected.getReference(refIdx),
                         actual.getReference(refIdx),
                         caseSensitive);
        }
    }

    /**
     * Asserts that the two given references are equal.
     * 
     * @param expected      The expected reference
     * @param actual        The actual reference
     * @param caseSensitive Whether case matters when comparing
     */
    protected void assertEquals(Reference expected, Reference actual, boolean caseSensitive)
    {
        if (caseSensitive)
        {
            assertEquals("Local column names do not match.",
                         getPlatform().getSqlBuilder().shortenName(expected.getLocalColumnName(), getSqlBuilder().getMaxColumnNameLength()),
                         getPlatform().getSqlBuilder().shortenName(actual.getLocalColumnName(), getSqlBuilder().getMaxColumnNameLength()));
            assertEquals("Foreign column names do not match.",
                         getPlatform().getSqlBuilder().shortenName(expected.getForeignColumnName(), getSqlBuilder().getMaxColumnNameLength()),
                         getPlatform().getSqlBuilder().shortenName(actual.getForeignColumnName(), getSqlBuilder().getMaxColumnNameLength()));
        }
        else
        {
            assertEquals("Local column names do not match (ignoring case).",
                         getPlatform().getSqlBuilder().shortenName(expected.getLocalColumnName().toUpperCase(), getSqlBuilder().getMaxColumnNameLength()),
                         getPlatform().getSqlBuilder().shortenName(actual.getLocalColumnName().toUpperCase(), getSqlBuilder().getMaxColumnNameLength()));
            assertEquals("Foreign column names do not match (ignoring case).",
                         getPlatform().getSqlBuilder().shortenName(expected.getForeignColumnName().toUpperCase(), getSqlBuilder().getMaxColumnNameLength()),
                         getPlatform().getSqlBuilder().shortenName(actual.getForeignColumnName().toUpperCase(), getSqlBuilder().getMaxColumnNameLength()));
        }
    }

    /**
     * Asserts that the two given indices are equal.
     * 
     * @param expected      The expected index
     * @param actual        The actual index
     * @param caseSensitive Whether case matters when comparing
     */
    protected void assertEquals(Index expected, Index actual, boolean caseSensitive)
    {
        if (caseSensitive)
        {
            assertEquals("Index names do not match.",
                         getPlatform().getSqlBuilder().shortenName(expected.getName(), getSqlBuilder().getMaxConstraintNameLength()),
                         getPlatform().getSqlBuilder().shortenName(actual.getName(), getSqlBuilder().getMaxConstraintNameLength()));
        }
        else
        {
            assertEquals("Index names do not match (ignoring case).",
                         getPlatform().getSqlBuilder().shortenName(expected.getName().toUpperCase(), getSqlBuilder().getMaxConstraintNameLength()),
                         getPlatform().getSqlBuilder().shortenName(actual.getName().toUpperCase(), getSqlBuilder().getMaxConstraintNameLength()));
        }
        assertEquals("Unique status not the same for index "+actual.getName()+".",
                     expected.isUnique(),
                     actual.isUnique());
        assertEquals("Not the same number of columns in index "+actual.getName()+".",
                     expected.getColumnCount(),
                     actual.getColumnCount());
        for (int columnIdx = 0; columnIdx < actual.getColumnCount(); columnIdx++)
        {
            assertEquals(expected.getColumn(columnIdx),
                         actual.getColumn(columnIdx),
                         caseSensitive);
        }
    }

    /**
     * Asserts that the two given index columns are equal.
     * 
     * @param expected      The expected index column
     * @param actual        The actual index column
     * @param caseSensitive Whether case matters when comparing
     */
    protected void assertEquals(IndexColumn expected, IndexColumn actual, boolean caseSensitive)
    {
        if (caseSensitive)
        {
            assertEquals("Index column names do not match.",
                         getPlatform().getSqlBuilder().shortenName(expected.getName(), getSqlBuilder().getMaxColumnNameLength()),
                         getPlatform().getSqlBuilder().shortenName(actual.getName(), getSqlBuilder().getMaxColumnNameLength()));
        }
        else
        {
            assertEquals("Index column names do not match (ignoring case).",
                         getPlatform().getSqlBuilder().shortenName(expected.getName().toUpperCase(), getSqlBuilder().getMaxColumnNameLength()),
                         getPlatform().getSqlBuilder().shortenName(actual.getName().toUpperCase(), getSqlBuilder().getMaxColumnNameLength()));
        }
        assertEquals("Size not the same for index column "+actual.getName()+".",
                     expected.getSize(),
                     actual.getSize());
    }
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.