All Downloads are FREE. Search and download functionalities are using the official Maven repository.

liquibase.sqlgenerator.ext.CreateTableGenerator Maven / Gradle / Ivy

There is a newer version: 2.0.1
Show newest version
// Copyright 2011 Leo Przybylski. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
//    1. Redistributions of source code must retain the above copyright notice, this list of
//       conditions and the following disclaimer.
//
//    2. Redistributions in binary form must reproduce the above copyright notice, this list
//       of conditions and the following disclaimer in the documentation and/or other materials
//       provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY  ''AS IS'' AND ANY EXPRESS OR IMPLIED
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL  OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// The views and conclusions contained in the software and documentation are those of the
// authors and should not be interpreted as representing official policies, either expressed
// or implied, of Leo Przybylski.
package liquibase.sqlgenerator.ext;

import liquibase.database.Database;
import liquibase.database.core.*;
import liquibase.exception.ValidationErrors;
import liquibase.logging.LogFactory;
import liquibase.sql.Sql;
import liquibase.sql.UnparsedSql;
import liquibase.sqlgenerator.SqlGeneratorChain;
import liquibase.statement.AutoIncrementConstraint;
import liquibase.statement.ForeignKeyConstraint;
import liquibase.statement.UniqueConstraint;
import liquibase.statement.core.CreateTableStatement;
import liquibase.util.StringUtils;
import java.util.Iterator;
import java.math.BigDecimal;

import static liquibase.ext.Constants.EXTENSION_PRIORITY;

/**
 *
 * @author Leo Przybylski (leo [at] rsmart.com)
 */
public class CreateTableGenerator extends liquibase.sqlgenerator.core.CreateTableGenerator {

    @Override
    public int getPriority() {
        return EXTENSION_PRIORITY;
    }
    
    @Override
    public ValidationErrors validate(CreateTableStatement createTableStatement, Database database, SqlGeneratorChain sqlGeneratorChain) {
        ValidationErrors validationErrors = new ValidationErrors();
        validationErrors.checkRequiredField("tableName", createTableStatement.getTableName());
        validationErrors.checkRequiredField("columns", createTableStatement.getColumns());
        return validationErrors;
    }

    @Override
    public Sql[] generateSql(CreateTableStatement statement, Database database, SqlGeneratorChain sqlGeneratorChain) {
            StringBuffer buffer = new StringBuffer();
        buffer.append("CREATE TABLE ").append(database.escapeTableName(null, statement.getTableName())).append(" ");
        buffer.append("(");
        
        boolean isSinglePrimaryKeyColumn = statement.getPrimaryKeyConstraint() != null
            && statement.getPrimaryKeyConstraint().getColumns().size() == 1;
        
        boolean isPrimaryKeyAutoIncrement = false;
        
        Iterator columnIterator = statement.getColumns().iterator();
        while (columnIterator.hasNext()) {
            String column = columnIterator.next();
            
            buffer.append(database.escapeColumnName(null, statement.getTableName(), column));
            buffer.append(" ").append(statement.getColumnTypes().get(column));
            
            AutoIncrementConstraint autoIncrementConstraint = null;
            
            for (AutoIncrementConstraint currentAutoIncrementConstraint : statement.getAutoIncrementConstraints()) {
                if (column.equals(currentAutoIncrementConstraint.getColumnName())) {
                        autoIncrementConstraint = currentAutoIncrementConstraint;
                        break;
                }
            }

            boolean isAutoIncrementColumn = autoIncrementConstraint != null;            
            boolean isPrimaryKeyColumn = statement.getPrimaryKeyConstraint() != null
                        && statement.getPrimaryKeyConstraint().getColumns().contains(column);
            isPrimaryKeyAutoIncrement = isPrimaryKeyAutoIncrement
                        || isPrimaryKeyColumn && isAutoIncrementColumn;
            
            if ((database instanceof SQLiteDatabase) &&
                                        isSinglePrimaryKeyColumn &&
                                        isPrimaryKeyColumn &&
                                        isAutoIncrementColumn) {
                String pkName = StringUtils.trimToNull(statement.getPrimaryKeyConstraint().getConstraintName());
                    if (pkName == null) {
                        pkName = database.generatePrimaryKeyName(statement.getTableName());
                    }
                if (pkName != null) {
                    buffer.append(" CONSTRAINT ");
                    buffer.append(database.escapeConstraintName(pkName));
                }
                buffer.append(" PRIMARY KEY AUTOINCREMENT");
                        }

            // No default for mysql date
            if ((database instanceof MySQLDatabase) 
                && statement.getColumnTypes().get(column).toString().startsWith("DATE")) {
                statement.getDefaultValues().put(column, null);
            }

            if (statement.getDefaultValue(column) != null) {
                Object defaultValue = statement.getDefaultValue(column);
                
                if ((statement.getColumnTypes().get(column).toString().startsWith("DECIMAL")
                     || statement.getColumnTypes().get(column).toString().startsWith("NUMERIC"))
                    && !"null".equalsIgnoreCase(defaultValue.toString())) {
                    int[] bounds = parseBounds(statement.getColumnTypes().get(column).toString());
                    BigDecimal parsedValue = new BigDecimal(defaultValue.toString());
                    
                    StringBuilder max = new StringBuilder();
                    for (int i = 0; i < bounds[0] - bounds[1]; i++) max.append("9");
                    
                    if (bounds[1] > 0) {
                        max.append(".");
                    }
                    for (int i = 0; i < bounds[1]; i++) max.append("9");
                    
                    if (parsedValue.compareTo(new BigDecimal(max.toString())) > 0) {
                        defaultValue = max;
                    }
                }
                if (database instanceof MSSQLDatabase) {
                    buffer.append(" CONSTRAINT ").append(((MSSQLDatabase) database).generateDefaultConstraintName(statement.getTableName(), column));
                }
                buffer.append(" DEFAULT ");
                buffer.append(statement.getColumnTypes().get(column).convertObjectToString(defaultValue, database));
            }

            if (isAutoIncrementColumn) {
                // TODO: check if database supports auto increment on non primary key column
                if (database.supportsAutoIncrement()) {
                        String autoIncrementClause = database.getAutoIncrementClause(autoIncrementConstraint.getStartWith(), autoIncrementConstraint.getIncrementBy());
                
                        if (!"".equals(autoIncrementClause)) {
                                buffer.append(" ").append(autoIncrementClause);
                        }
                } else {
                    LogFactory.getLogger().warning(database.getTypeName()+" does not support autoincrement columns as request for "+(database.escapeTableName(null, statement.getTableName())));
                }
            }

            if (statement.getNotNullColumns().contains(column)) {
                buffer.append(" NOT NULL");
            } else {
                if (database instanceof SybaseDatabase || database instanceof SybaseASADatabase) {
                    buffer.append(" NULL");
                }
            }

            if (database instanceof InformixDatabase && isSinglePrimaryKeyColumn) {
                buffer.append(" PRIMARY KEY");
            }

            if (columnIterator.hasNext()) {
                buffer.append(", ");
            }
        }

        buffer.append(",");

        // TODO informixdb
        if (!( (database instanceof SQLiteDatabase) &&
                                isSinglePrimaryKeyColumn &&
                                isPrimaryKeyAutoIncrement) &&

                                !((database instanceof InformixDatabase) &&
                                isSinglePrimaryKeyColumn
                                )) {
                // ...skip this code block for sqlite if a single column primary key
                // with an autoincrement constraint exists.
                // This constraint is added after the column type.

                if (statement.getPrimaryKeyConstraint() != null && statement.getPrimaryKeyConstraint().getColumns().size() > 0) {
                        if (!(database instanceof InformixDatabase)) {
                            String pkName = StringUtils.trimToNull(statement.getPrimaryKeyConstraint().getConstraintName());
                            if (pkName == null) {
                                // TODO ORA-00972: identifier is too long
                                    // If tableName lenght is more then 28 symbols
                                    // then generated pkName will be incorrect
                                    pkName = database.generatePrimaryKeyName(statement.getTableName());
                            }
                    if (pkName != null) {
                        buffer.append(" CONSTRAINT ");
                        buffer.append(database.escapeConstraintName(pkName));
                    }
                }
                    buffer.append(" PRIMARY KEY (");
                    buffer.append(database.escapeColumnNameList(StringUtils.join(statement.getPrimaryKeyConstraint().getColumns(), ", ")));
                    buffer.append(")");
                        // Setting up table space for PK's index if it exist
                        if (database instanceof OracleDatabase &&
                            statement.getPrimaryKeyConstraint().getTablespace() != null) {
                                buffer.append(" USING INDEX TABLESPACE ");
                                buffer.append(statement.getPrimaryKeyConstraint().getTablespace());
                        }
                    buffer.append(",");
                }
        }

        for (ForeignKeyConstraint fkConstraint : statement.getForeignKeyConstraints()) {
                if (!(database instanceof InformixDatabase)) {
                        buffer.append(" CONSTRAINT ");
                buffer.append(database.escapeConstraintName(fkConstraint.getForeignKeyName()));
                }
            String referencesString = fkConstraint.getReferences();
            buffer.append(" FOREIGN KEY (")
                    .append(database.escapeColumnName(null, statement.getTableName(), fkConstraint.getColumn()))
                    .append(") REFERENCES ")
                    .append(referencesString);

            if (fkConstraint.isDeleteCascade()) {
                buffer.append(" ON DELETE CASCADE");
            }

            if ((database instanceof InformixDatabase)) {
                buffer.append(" CONSTRAINT ");
                buffer.append(database.escapeConstraintName(fkConstraint.getForeignKeyName()));
            }

            if (fkConstraint.isInitiallyDeferred()) {
                buffer.append(" INITIALLY DEFERRED");
            }
            if (fkConstraint.isDeferrable()) {
                buffer.append(" DEFERRABLE");
            }
            buffer.append(",");
        }

        for (UniqueConstraint uniqueConstraint : statement.getUniqueConstraints()) {
            if (uniqueConstraint.getConstraintName() != null && !constraintNameAfterUnique(database)) {
                buffer.append(" CONSTRAINT ");
                buffer.append(database.escapeConstraintName(uniqueConstraint.getConstraintName()));
            }
            buffer.append(" UNIQUE (");
            buffer.append(database.escapeColumnNameList(StringUtils.join(uniqueConstraint.getColumns(), ", ")));
            buffer.append(")");
            if (uniqueConstraint.getConstraintName() != null && constraintNameAfterUnique(database)) {
                buffer.append(" CONSTRAINT ");
                buffer.append(database.escapeConstraintName(uniqueConstraint.getConstraintName()));
            }
            buffer.append(",");
        }

//        if (constraints != null && constraints.getCheck() != null) {
//            buffer.append(constraints.getCheck()).append(" ");
//        }
//    }

        String sql = buffer.toString().replaceFirst(",\\s*$", "") + ")";

//        if (StringUtils.trimToNull(tablespace) != null && database.supportsTablespaces()) {
//            if (database instanceof MSSQLDatabase) {
//                buffer.append(" ON ").append(tablespace);
//            } else if (database instanceof DB2Database) {
//                buffer.append(" IN ").append(tablespace);
//            } else {
//                buffer.append(" TABLESPACE ").append(tablespace);
//            }
//        }

        if (statement.getTablespace() != null && database.supportsTablespaces()) {
            if (database instanceof MSSQLDatabase || database instanceof SybaseASADatabase) {
                sql += " ON " + statement.getTablespace();
            } else if (database instanceof DB2Database || database instanceof InformixDatabase) {
                sql += " IN " + statement.getTablespace();
            } else {
                sql += " TABLESPACE " + statement.getTablespace();
            }
        }

        if (database instanceof MySQLDatabase) {
            sql += " ENGINE = InnoDB ";
        }
        
        return new Sql[] {
            new UnparsedSql(sql)
        };
    }

    private boolean constraintNameAfterUnique(Database database) {
        return database instanceof InformixDatabase;
    }

    
    protected int[] parseBounds(final String decimal) {
        final int[] retval = new int[2];
        int comma = decimal.indexOf(",");

        try {
            if (comma < 0) {
                retval[0] = Integer.parseInt(decimal.substring(decimal.indexOf("(") + 1, decimal.lastIndexOf(")")));
                retval[1] = 0;
            }
            else {
                retval[0] = Integer.parseInt(decimal.substring(decimal.indexOf("(") + 1, comma));
                retval[1] = Integer.parseInt(decimal.substring(comma + 1, decimal.lastIndexOf(")")));
            }
        }
        catch (StringIndexOutOfBoundsException e) {
            debug("parsebounds " + decimal);
            throw e;
        }
        return retval;
    }

    protected void info(final String message) {
	LogFactory.getLogger().info(message);
    }

    protected void debug(final String message) {
	LogFactory.getLogger().debug(message);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy