Search in sources :

Example 1 with Column

use of liquibase.structure.core.Column in project liquibase by liquibase.

the class IndexExistsPrecondition method check.

@Override
public void check(Database database, DatabaseChangeLog changeLog, ChangeSet changeSet) throws PreconditionFailedException, PreconditionErrorException {
    try {
        Schema schema = new Schema(getCatalogName(), getSchemaName());
        Index example = new Index();
        String tableName = StringUtils.trimToNull(getTableName());
        if (tableName != null) {
            example.setTable((Table) new Table().setName(database.correctObjectName(getTableName(), Table.class)).setSchema(schema));
        }
        example.setName(database.correctObjectName(getIndexName(), Index.class));
        if (StringUtils.trimToNull(getColumnNames()) != null) {
            for (String column : getColumnNames().split("\\s*,\\s*")) {
                example.addColumn(new Column(database.correctObjectName(column, Column.class)));
            }
        }
        if (!SnapshotGeneratorFactory.getInstance().has(example, database)) {
            String name = "";
            if (getIndexName() != null) {
                name += database.escapeObjectName(getIndexName(), Index.class);
            }
            if (tableName != null) {
                name += " on " + database.escapeObjectName(getTableName(), Table.class);
                if (StringUtils.trimToNull(getColumnNames()) != null) {
                    name += " columns " + getColumnNames();
                }
            }
            throw new PreconditionFailedException("Index " + name + " does not exist", changeLog, this);
        }
    } catch (Exception e) {
        if (e instanceof PreconditionFailedException) {
            throw (((PreconditionFailedException) e));
        }
        throw new PreconditionErrorException(e, changeLog, this);
    }
}
Also used : Table(liquibase.structure.core.Table) Column(liquibase.structure.core.Column) Schema(liquibase.structure.core.Schema) Index(liquibase.structure.core.Index)

Example 2 with Column

use of liquibase.structure.core.Column in project liquibase by liquibase.

the class UnexpectedTableChangeGenerator method fixUnexpected.

@Override
public Change[] fixUnexpected(DatabaseObject unexpectedObject, DiffOutputControl control, Database referenceDatabase, Database comparisonDatabase, ChangeGeneratorChain chain) {
    Table unexpectedTable = (Table) unexpectedObject;
    DropTableChange change = new DropTableChange();
    change.setTableName(unexpectedTable.getName());
    if (control.getIncludeCatalog()) {
        change.setCatalogName(unexpectedTable.getSchema().getCatalogName());
    }
    if (control.getIncludeSchema()) {
        change.setSchemaName(unexpectedTable.getSchema().getName());
    }
    for (Column column : unexpectedTable.getColumns()) {
        control.setAlreadyHandledUnexpected(column);
    }
    ;
    control.setAlreadyHandledUnexpected(unexpectedTable.getPrimaryKey());
    for (Index index : unexpectedTable.getIndexes()) {
        control.setAlreadyHandledUnexpected(index);
    }
    control.setAlreadyHandledUnexpected(unexpectedTable.getPrimaryKey());
    if (unexpectedTable.getPrimaryKey() != null) {
        control.setAlreadyHandledUnexpected(unexpectedTable.getPrimaryKey().getBackingIndex());
    }
    return new Change[] { change };
}
Also used : Table(liquibase.structure.core.Table) Column(liquibase.structure.core.Column) DropTableChange(liquibase.change.core.DropTableChange) Index(liquibase.structure.core.Index) Change(liquibase.change.Change) DropTableChange(liquibase.change.core.DropTableChange)

Example 3 with Column

use of liquibase.structure.core.Column in project liquibase by liquibase.

the class UnexpectedViewChangeGenerator method fixUnexpected.

@Override
public Change[] fixUnexpected(DatabaseObject unexpectedObject, DiffOutputControl control, Database referenceDatabase, Database comparisonDatabase, ChangeGeneratorChain chain) {
    View view = (View) unexpectedObject;
    DropViewChange change = new DropViewChange();
    change.setViewName(view.getName());
    if (control.getIncludeCatalog()) {
        change.setCatalogName(view.getSchema().getCatalogName());
    }
    if (control.getIncludeSchema()) {
        change.setSchemaName(view.getSchema().getName());
    }
    for (Column column : view.getColumns()) {
        control.setAlreadyHandledUnexpected(column);
    }
    ;
    return new Change[] { change };
}
Also used : Column(liquibase.structure.core.Column) DropViewChange(liquibase.change.core.DropViewChange) DropViewChange(liquibase.change.core.DropViewChange) Change(liquibase.change.Change) View(liquibase.structure.core.View)

Example 4 with Column

use of liquibase.structure.core.Column in project liquibase by liquibase.

the class LoadDataChange method generateStatements.

@Override
public SqlStatement[] generateStatements(Database database) {
    boolean databaseSupportsBatchUpdates = false;
    try {
        if (!(database instanceof MySQLDatabase)) {
            // mysql supports batch updates, but the performance vs. the big insert is worse
            databaseSupportsBatchUpdates = database.supportsBatchUpdates();
        }
    } catch (DatabaseException e) {
        throw new UnexpectedLiquibaseException(e);
    }
    CSVReader reader = null;
    try {
        reader = getCSVReader();
        if (reader == null) {
            throw new UnexpectedLiquibaseException("Unable to read file " + this.getFile());
        }
        String[] headers = reader.readNext();
        if (headers == null) {
            throw new UnexpectedLiquibaseException("Data file " + getFile() + " was empty");
        }
        // Make sure all take the column list we interpolated from the CSV headers
        addColumnsFromHeaders(headers);
        // If we have an real JDBC connection to the database, ask the database for any missing column types.
        try {
            retrieveMissingColumnLoadTypes(columns, database);
        } catch (DatabaseException e) {
            throw new UnexpectedLiquibaseException(e);
        }
        List<ExecutablePreparedStatementBase> preparedStatements = new ArrayList<>();
        boolean anyPreparedStatements = false;
        String[] line;
        // Start at '1' to take into account the header (already processed):
        int lineNumber = 1;
        boolean isCommentingEnabled = StringUtil.isNotEmpty(commentLineStartsWith);
        List<SqlStatement> statements = new ArrayList<>();
        while ((line = reader.readNext()) != null) {
            lineNumber++;
            if ((line.length == 0) || ((line.length == 1) && (StringUtil.trimToNull(line[0]) == null)) || (isCommentingEnabled && isLineCommented(line))) {
                // nothing interesting on this line
                continue;
            }
            // (Failure could indicate unquoted strings with commas, for example).
            if (line.length != headers.length) {
                throw new UnexpectedLiquibaseException("CSV file " + getFile() + " Line " + lineNumber + " has " + line.length + " values defined, Header has " + headers.length + ". Numbers MUST be equal (check for unquoted string with embedded commas)");
            }
            boolean needsPreparedStatement = false;
            List<LoadDataColumnConfig> columnsFromCsv = new ArrayList<>();
            for (int i = 0; i < headers.length; i++) {
                String value = line[i];
                String columnName = headers[i].trim();
                LoadDataColumnConfig valueConfig = new LoadDataColumnConfig();
                LoadDataColumnConfig columnConfig = getColumnConfig(i, columnName);
                if (columnConfig != null) {
                    if ("skip".equalsIgnoreCase(columnConfig.getType())) {
                        continue;
                    }
                    // don't overwrite header name unless there is actually a value to override it with
                    if (columnConfig.getName() != null) {
                        columnName = columnConfig.getName();
                    }
                    // 
                    if ("NULL".equalsIgnoreCase(value)) {
                        valueConfig.setType(columnConfig.getType());
                    }
                    valueConfig.setName(columnName);
                    valueConfig.setAllowUpdate(columnConfig.getAllowUpdate());
                    if (value.isEmpty()) {
                        value = columnConfig.getDefaultValue();
                    }
                    if (StringUtil.equalsWordNull(value)) {
                        valueConfig.setValue(null);
                    } else if (columnConfig.getType() == null) {
                        // columnConfig did not specify a type
                        valueConfig.setValue(value);
                    } else if (columnConfig.getTypeEnum() == LOAD_DATA_TYPE.UNKNOWN) {
                        // columnConfig did not match a specific type
                        valueConfig.setValue(value);
                    } else if (columnConfig.getTypeEnum() == LOAD_DATA_TYPE.BOOLEAN) {
                        if (value == null) {
                            // TODO getDefaultValueBoolean should use BooleanUtil.parseBoolean also for consistent behaviour
                            valueConfig.setValueBoolean(columnConfig.getDefaultValueBoolean());
                        } else {
                            valueConfig.setValueBoolean(BooleanUtil.parseBoolean(value));
                        }
                    } else if (columnConfig.getTypeEnum() == LOAD_DATA_TYPE.NUMERIC) {
                        if (value != null) {
                            valueConfig.setValueNumeric(value);
                        } else {
                            valueConfig.setValueNumeric(columnConfig.getDefaultValueNumeric());
                        }
                    } else if (columnConfig.getType().toLowerCase().contains("date") || columnConfig.getType().toLowerCase().contains("time")) {
                        if ("NULL".equalsIgnoreCase(value) || "".equals(value)) {
                            valueConfig.setValue(null);
                        } else {
                            try {
                                // Need the column type for handling 'NOW' or 'TODAY' type column value
                                valueConfig.setType(columnConfig.getType());
                                if (value != null) {
                                    valueConfig.setValueDate(value);
                                } else {
                                    valueConfig.setValueDate(columnConfig.getDefaultValueDate());
                                }
                            } catch (DateParseException e) {
                                throw new UnexpectedLiquibaseException(e);
                            }
                        }
                    } else if (columnConfig.getTypeEnum() == LOAD_DATA_TYPE.STRING) {
                        valueConfig.setType(columnConfig.getType());
                        valueConfig.setValue(value == null ? "" : value);
                    } else if (columnConfig.getTypeEnum() == LOAD_DATA_TYPE.COMPUTED) {
                        if (null != value) {
                            liquibase.statement.DatabaseFunction function = new liquibase.statement.DatabaseFunction(value);
                            valueConfig.setValueComputed(function);
                        } else {
                            valueConfig.setValueComputed(columnConfig.getDefaultValueComputed());
                        }
                    } else if (columnConfig.getTypeEnum() == LOAD_DATA_TYPE.SEQUENCE) {
                        if (value == null) {
                            throw new UnexpectedLiquibaseException("Must set a sequence name in the loadData column defaultValue attribute");
                        }
                        liquibase.statement.SequenceNextValueFunction function = new liquibase.statement.SequenceNextValueFunction(getSchemaName(), value);
                        valueConfig.setValueComputed(function);
                    } else if (columnConfig.getType().equalsIgnoreCase(LOAD_DATA_TYPE.BLOB.toString())) {
                        if ("NULL".equalsIgnoreCase(value)) {
                            valueConfig.setValue(null);
                        } else if (BASE64_PATTERN.matcher(value).matches()) {
                            valueConfig.setType(columnConfig.getType());
                            valueConfig.setValue(value);
                            needsPreparedStatement = true;
                        } else {
                            valueConfig.setValueBlobFile(value);
                            needsPreparedStatement = true;
                        }
                    } else if (columnConfig.getTypeEnum() == LOAD_DATA_TYPE.CLOB) {
                        valueConfig.setValueClobFile(value);
                        needsPreparedStatement = true;
                    } else if (columnConfig.getTypeEnum() == LOAD_DATA_TYPE.UUID) {
                        valueConfig.setType(columnConfig.getType());
                        if ("NULL".equalsIgnoreCase(value)) {
                            valueConfig.setValue(null);
                        } else {
                            valueConfig.setValue(value);
                        }
                    } else if (columnConfig.getType().equalsIgnoreCase(LOAD_DATA_TYPE.OTHER.toString())) {
                        valueConfig.setType(columnConfig.getType());
                        if ("NULL".equalsIgnoreCase(value)) {
                            valueConfig.setValue(null);
                        } else {
                            valueConfig.setValue(value);
                        }
                    } else {
                        throw new UnexpectedLiquibaseException(String.format(coreBundle.getString("loaddata.type.is.not.supported"), columnConfig.getType()));
                    }
                } else {
                    // No columnConfig found. Assume header column name to be the table column name.
                    if (columnName.contains("(") || (columnName.contains(")") && (database instanceof AbstractJdbcDatabase))) {
                        columnName = ((AbstractJdbcDatabase) database).quoteObject(columnName, Column.class);
                    }
                    valueConfig.setName(columnName);
                    valueConfig.setValue(getValueToWrite(value));
                }
                columnsFromCsv.add(valueConfig);
            }
            // end of: iterate through all the columns of a CSV line
            // Try to use prepared statements if any of the following conditions apply:
            // 1. There is no other option than using a prepared statement (e.g. in cases of LOBs) regardless
            // of whether the 'usePreparedStatement' is set to false
            // 2. The database supports batched statements (for improved performance) AND we are not in an
            // "SQL" mode (i.e. we generate an SQL file instead of actually modifying the database).
            // BUT: if the user specifically requests usePreparedStatement=false, then respect that
            boolean actuallyUsePreparedStatements = false;
            if (hasPreparedStatementsImplemented()) {
                if (usePreparedStatements != null) {
                    if (!usePreparedStatements && needsPreparedStatement) {
                        throw new UnexpectedLiquibaseException("loadData is requesting usePreparedStatements=false but prepared statements are required");
                    }
                    actuallyUsePreparedStatements = usePreparedStatements;
                } else {
                    actuallyUsePreparedStatements = needsPreparedStatement || (databaseSupportsBatchUpdates && !isLoggingExecutor(database));
                }
            }
            if (actuallyUsePreparedStatements) {
                anyPreparedStatements = true;
                ExecutablePreparedStatementBase stmt = this.createPreparedStatement(database, getCatalogName(), getSchemaName(), getTableName(), columnsFromCsv, getChangeSet(), Scope.getCurrentScope().getResourceAccessor());
                preparedStatements.add(stmt);
            } else {
                InsertStatement insertStatement = this.createStatement(getCatalogName(), getSchemaName(), getTableName());
                for (LoadDataColumnConfig column : columnsFromCsv) {
                    String columnName = column.getName();
                    Object value = column.getValueObject();
                    if (value == null) {
                        value = "NULL";
                    }
                    insertStatement.addColumnValue(columnName, value);
                    if (insertStatement instanceof InsertOrUpdateStatement) {
                        ((InsertOrUpdateStatement) insertStatement).setAllowColumnUpdate(columnName, column.getAllowUpdate() == null || column.getAllowUpdate());
                    }
                }
                statements.add(insertStatement);
            }
        // end of: will we use a PreparedStatement?
        }
        if (anyPreparedStatements) {
            // If we have only prepared statements and the database supports batching, let's roll
            if (databaseSupportsBatchUpdates && statements.isEmpty() && (!preparedStatements.isEmpty())) {
                if (database instanceof PostgresDatabase) {
                    // we don't do batch updates for Postgres but we still send as a prepared statement, see LB-744
                    return preparedStatements.toArray(new SqlStatement[preparedStatements.size()]);
                } else {
                    return new SqlStatement[] { new BatchDmlExecutablePreparedStatement(database, getCatalogName(), getSchemaName(), getTableName(), columns, getChangeSet(), Scope.getCurrentScope().getResourceAccessor(), preparedStatements) };
                }
            } else {
                return statements.toArray(new SqlStatement[statements.size()]);
            }
        } else {
            if (statements.isEmpty()) {
                // avoid returning unnecessary dummy statement
                return new SqlStatement[0];
            }
            InsertSetStatement statementSet = this.createStatementSet(getCatalogName(), getSchemaName(), getTableName());
            for (SqlStatement stmt : statements) {
                statementSet.addInsertStatement((InsertStatement) stmt);
            }
            if ((database instanceof MSSQLDatabase) || (database instanceof MySQLDatabase) || (database instanceof PostgresDatabase)) {
                List<InsertStatement> innerStatements = statementSet.getStatements();
                if ((innerStatements != null) && (!innerStatements.isEmpty()) && (innerStatements.get(0) instanceof InsertOrUpdateStatement)) {
                    // cannot do insert or update in a single statement
                    return statementSet.getStatementsArray();
                }
                // should the need arise, on generation.
                return new SqlStatement[] { statementSet };
            } else {
                return statementSet.getStatementsArray();
            }
        }
    } catch (CsvMalformedLineException e) {
        throw new RuntimeException("Error parsing " + getRelativeTo() + " on line " + e.getLineNumber() + ": " + e.getMessage());
    } catch (IOException | LiquibaseException e) {
        throw new RuntimeException(e);
    } catch (UnexpectedLiquibaseException ule) {
        if ((getChangeSet() != null) && (getChangeSet().getFailOnError() != null) && !getChangeSet().getFailOnError()) {
            LOG.info("Change set " + getChangeSet().toString(false) + " failed, but failOnError was false.  Error: " + ule.getMessage());
            return new SqlStatement[0];
        } else {
            throw ule;
        }
    } finally {
        if (null != reader) {
            try {
                reader.close();
            } catch (Exception e) {
            // Do nothing
            }
        }
    }
}
Also used : InsertOrUpdateStatement(liquibase.statement.core.InsertOrUpdateStatement) CsvMalformedLineException(com.opencsv.exceptions.CsvMalformedLineException) InsertStatement(liquibase.statement.core.InsertStatement) SqlStatement(liquibase.statement.SqlStatement) BatchDmlExecutablePreparedStatement(liquibase.statement.BatchDmlExecutablePreparedStatement) Column(liquibase.structure.core.Column) MSSQLDatabase(liquibase.database.core.MSSQLDatabase) CSVReader(liquibase.util.csv.CSVReader) MySQLDatabase(liquibase.database.core.MySQLDatabase) IOException(java.io.IOException) AbstractJdbcDatabase(liquibase.database.AbstractJdbcDatabase) CsvMalformedLineException(com.opencsv.exceptions.CsvMalformedLineException) InvalidExampleException(liquibase.snapshot.InvalidExampleException) IOException(java.io.IOException) ExecutablePreparedStatementBase(liquibase.statement.ExecutablePreparedStatementBase) PostgresDatabase(liquibase.database.core.PostgresDatabase) InsertSetStatement(liquibase.statement.core.InsertSetStatement)

Example 5 with Column

use of liquibase.structure.core.Column in project liquibase by liquibase.

the class DropNotNullConstraintChange method checkStatus.

@Override
public ChangeStatus checkStatus(Database database) {
    try {
        Column snapshot = SnapshotGeneratorFactory.getInstance().createSnapshot(new Column(Table.class, getCatalogName(), getSchemaName(), getTableName(), getColumnName()), database);
        Boolean nullable = snapshot.isNullable();
        return new ChangeStatus().assertComplete((nullable == null) || nullable, "Column is not null");
    } catch (Exception e) {
        return new ChangeStatus().unknown(e);
    }
}
Also used : Table(liquibase.structure.core.Table) Column(liquibase.structure.core.Column)

Aggregations

Column (liquibase.structure.core.Column)45 Table (liquibase.structure.core.Table)24 SnapshotControl (liquibase.snapshot.SnapshotControl)10 Change (liquibase.change.Change)8 Index (liquibase.structure.core.Index)8 DatabaseSnapshot (liquibase.snapshot.DatabaseSnapshot)7 SqlStatement (liquibase.statement.SqlStatement)7 ArrayList (java.util.ArrayList)6 DatabaseObject (liquibase.structure.DatabaseObject)6 Test (org.junit.Test)6 DatabaseException (liquibase.exception.DatabaseException)5 Schema (liquibase.structure.core.Schema)5 UniqueConstraint (liquibase.structure.core.UniqueConstraint)5 Database (liquibase.database.Database)4 MSSQLDatabase (liquibase.database.core.MSSQLDatabase)4 UnexpectedLiquibaseException (liquibase.exception.UnexpectedLiquibaseException)4 CatalogAndSchema (liquibase.CatalogAndSchema)3 Liquibase (liquibase.Liquibase)3 AbstractIntegrationTest (liquibase.dbtest.AbstractIntegrationTest)3 Executor (liquibase.executor.Executor)3