Search in sources :

Example 1 with InsertStatement

use of liquibase.statement.core.InsertStatement 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 2 with InsertStatement

use of liquibase.statement.core.InsertStatement in project liquibase by liquibase.

the class InsertDataChange method generateStatements.

@Override
public SqlStatement[] generateStatements(Database database) {
    boolean needsPreparedStatement = false;
    for (ColumnConfig column : columns) {
        if (column.getValueBlobFile() != null) {
            needsPreparedStatement = true;
        }
        if (column.getValueClobFile() != null) {
            needsPreparedStatement = true;
        }
        if (LoadDataChange.LOAD_DATA_TYPE.BLOB.name().equalsIgnoreCase(column.getType())) {
            needsPreparedStatement = true;
        }
        if (LoadDataChange.LOAD_DATA_TYPE.CLOB.name().equalsIgnoreCase(column.getType())) {
            needsPreparedStatement = true;
        }
        if (!needsPreparedStatement && (database instanceof InformixDatabase)) {
            if (column.getValue() != null) {
                try {
                    Column snapshot = SnapshotGeneratorFactory.getInstance().createSnapshot(new Column(column), database);
                    if (snapshot != null) {
                        needsPreparedStatement = true;
                    }
                } catch (Exception ignore) {
                // assume it's not a clob
                }
            }
        }
    }
    if (needsPreparedStatement) {
        return new SqlStatement[] { new InsertExecutablePreparedStatement(database, catalogName, schemaName, tableName, columns, getChangeSet(), Scope.getCurrentScope().getResourceAccessor()) };
    }
    InsertStatement statement = new InsertStatement(getCatalogName(), getSchemaName(), getTableName());
    for (ColumnConfig column : columns) {
        if (database.supportsAutoIncrement() && (column.isAutoIncrement() != null) && column.isAutoIncrement()) {
            // skip auto increment columns as they will be generated by the database
            continue;
        }
        final Object valueObject = column.getValueObject();
        if (valueObject instanceof SequenceNextValueFunction) {
            ((SequenceNextValueFunction) valueObject).setSchemaName(this.getSchemaName());
        }
        if (valueObject instanceof SequenceCurrentValueFunction) {
            ((SequenceCurrentValueFunction) valueObject).setSchemaName(this.getSchemaName());
        }
        statement.addColumnValue(column.getName(), column.getValueObject());
    }
    return new SqlStatement[] { statement };
}
Also used : SequenceCurrentValueFunction(liquibase.statement.SequenceCurrentValueFunction) SqlStatement(liquibase.statement.SqlStatement) InformixDatabase(liquibase.database.core.InformixDatabase) Column(liquibase.structure.core.Column) SequenceNextValueFunction(liquibase.statement.SequenceNextValueFunction) InsertExecutablePreparedStatement(liquibase.statement.InsertExecutablePreparedStatement) InsertStatement(liquibase.statement.core.InsertStatement)

Example 3 with InsertStatement

use of liquibase.statement.core.InsertStatement in project liquibase by liquibase.

the class InsertSetGenerator method generateHeader.

public void generateHeader(StringBuilder sql, InsertSetStatement statement, Database database) {
    InsertStatement insert = statement.peek();
    getInsertGenerator(database).generateHeader(sql, insert, database);
}
Also used : InsertStatement(liquibase.statement.core.InsertStatement)

Example 4 with InsertStatement

use of liquibase.statement.core.InsertStatement in project liquibase by liquibase.

the class InsertSetGenerator method generateSql.

@Override
public Sql[] generateSql(InsertSetStatement statement, Database database, SqlGeneratorChain sqlGeneratorChain) {
    if (statement.peek() == null) {
        return new UnparsedSql[0];
    }
    StringBuilder sql = new StringBuilder();
    generateHeader(sql, statement, database);
    ArrayList<Sql> result = new ArrayList<>();
    int index = 0;
    for (InsertStatement sttmnt : statement.getStatements()) {
        index++;
        getInsertGenerator(database).generateValues(sql, sttmnt, database);
        sql.append(",");
        if (index > statement.getBatchThreshold()) {
            result.add(completeStatement(statement, sql));
            index = 0;
            sql.setLength(0);
            generateHeader(sql, statement, database);
        }
    }
    if (index > 0) {
        result.add(completeStatement(statement, sql));
    }
    return result.toArray(new UnparsedSql[result.size()]);
}
Also used : UnparsedSql(liquibase.sql.UnparsedSql) ArrayList(java.util.ArrayList) InsertStatement(liquibase.statement.core.InsertStatement) Sql(liquibase.sql.Sql) UnparsedSql(liquibase.sql.UnparsedSql)

Example 5 with InsertStatement

use of liquibase.statement.core.InsertStatement in project liquibase by liquibase.

the class MarkChangeSetRanGenerator method generateSql.

@Override
public Sql[] generateSql(MarkChangeSetRanStatement statement, Database database, SqlGeneratorChain sqlGeneratorChain) {
    String dateValue = database.getCurrentDateTimeFunction();
    ChangeSet changeSet = statement.getChangeSet();
    SqlStatement runStatement;
    // use LEGACY quoting since we're dealing with system objects
    ObjectQuotingStrategy currentStrategy = database.getObjectQuotingStrategy();
    database.setObjectQuotingStrategy(ObjectQuotingStrategy.LEGACY);
    try {
        try {
            if (statement.getExecType().equals(ChangeSet.ExecType.FAILED) || statement.getExecType().equals(ChangeSet.ExecType.SKIPPED)) {
                // don't mark
                return new Sql[0];
            }
            String tag = null;
            for (Change change : changeSet.getChanges()) {
                if (change instanceof TagDatabaseChange) {
                    TagDatabaseChange tagChange = (TagDatabaseChange) change;
                    tag = tagChange.getTag();
                }
            }
            if (statement.getExecType().ranBefore) {
                runStatement = new UpdateStatement(database.getLiquibaseCatalogName(), database.getLiquibaseSchemaName(), database.getDatabaseChangeLogTableName()).addNewColumnValue("DATEEXECUTED", new DatabaseFunction(dateValue)).addNewColumnValue("ORDEREXECUTED", ChangeLogHistoryServiceFactory.getInstance().getChangeLogService(database).getNextSequenceValue()).addNewColumnValue("MD5SUM", changeSet.generateCheckSum().toString()).addNewColumnValue("EXECTYPE", statement.getExecType().value).addNewColumnValue("DEPLOYMENT_ID", ChangeLogHistoryServiceFactory.getInstance().getChangeLogService(database).getDeploymentId()).addNewColumnValue(COMMENTS, getCommentsColumn(changeSet)).addNewColumnValue(CONTEXTS, getContextsColumn(changeSet)).addNewColumnValue(LABELS, getLabelsColumn(changeSet)).setWhereClause(database.escapeObjectName("ID", LiquibaseColumn.class) + " = ? " + "AND " + database.escapeObjectName("AUTHOR", LiquibaseColumn.class) + " = ? " + "AND " + database.escapeObjectName("FILENAME", LiquibaseColumn.class) + " = ?").addWhereParameters(changeSet.getId(), changeSet.getAuthor(), changeSet.getFilePath());
                if (tag != null) {
                    ((UpdateStatement) runStatement).addNewColumnValue("TAG", tag);
                }
            } else {
                runStatement = new InsertStatement(database.getLiquibaseCatalogName(), database.getLiquibaseSchemaName(), database.getDatabaseChangeLogTableName()).addColumnValue("ID", changeSet.getId()).addColumnValue("AUTHOR", changeSet.getAuthor()).addColumnValue("FILENAME", changeSet.getFilePath()).addColumnValue("DATEEXECUTED", new DatabaseFunction(dateValue)).addColumnValue("ORDEREXECUTED", ChangeLogHistoryServiceFactory.getInstance().getChangeLogService(database).getNextSequenceValue()).addColumnValue("MD5SUM", changeSet.generateCheckSum().toString()).addColumnValue("DESCRIPTION", limitSize(changeSet.getDescription())).addColumnValue(COMMENTS, getCommentsColumn(changeSet)).addColumnValue("EXECTYPE", statement.getExecType().value).addColumnValue(CONTEXTS, getContextsColumn(changeSet)).addColumnValue(LABELS, getLabelsColumn(changeSet)).addColumnValue("LIQUIBASE", StringUtil.limitSize(LiquibaseUtil.getBuildVersion().replaceAll("SNAPSHOT", "SNP").replaceAll("beta", "b").replaceAll("alpha", "b"), 20)).addColumnValue("DEPLOYMENT_ID", ChangeLogHistoryServiceFactory.getInstance().getChangeLogService(database).getDeploymentId());
                if (tag != null) {
                    ((InsertStatement) runStatement).addColumnValue("TAG", tag);
                }
            }
        } catch (LiquibaseException e) {
            throw new UnexpectedLiquibaseException(e);
        }
        return SqlGeneratorFactory.getInstance().generateSql(runStatement, database);
    } finally {
        database.setObjectQuotingStrategy(currentStrategy);
    }
}
Also used : UpdateStatement(liquibase.statement.core.UpdateStatement) DatabaseFunction(liquibase.statement.DatabaseFunction) TagDatabaseChange(liquibase.change.core.TagDatabaseChange) Change(liquibase.change.Change) TagDatabaseChange(liquibase.change.core.TagDatabaseChange) InsertStatement(liquibase.statement.core.InsertStatement) Sql(liquibase.sql.Sql) SqlStatement(liquibase.statement.SqlStatement) LiquibaseColumn(liquibase.changelog.column.LiquibaseColumn) LiquibaseException(liquibase.exception.LiquibaseException) UnexpectedLiquibaseException(liquibase.exception.UnexpectedLiquibaseException) ChangeSet(liquibase.changelog.ChangeSet) UnexpectedLiquibaseException(liquibase.exception.UnexpectedLiquibaseException) ObjectQuotingStrategy(liquibase.database.ObjectQuotingStrategy)

Aggregations

InsertStatement (liquibase.statement.core.InsertStatement)11 Sql (liquibase.sql.Sql)6 SequenceNextValueFunction (liquibase.statement.SequenceNextValueFunction)4 SqlStatement (liquibase.statement.SqlStatement)4 ColumnConfig (liquibase.change.ColumnConfig)3 PostgresDatabase (liquibase.database.core.PostgresDatabase)3 ArrayList (java.util.ArrayList)2 ObjectQuotingStrategy (liquibase.database.ObjectQuotingStrategy)2 SequenceCurrentValueFunction (liquibase.statement.SequenceCurrentValueFunction)2 Column (liquibase.structure.core.Column)2 Test (org.junit.Test)2 CsvMalformedLineException (com.opencsv.exceptions.CsvMalformedLineException)1 IOException (java.io.IOException)1 Change (liquibase.change.Change)1 TagDatabaseChange (liquibase.change.core.TagDatabaseChange)1 ChangeSet (liquibase.changelog.ChangeSet)1 LiquibaseColumn (liquibase.changelog.column.LiquibaseColumn)1 AbstractJdbcDatabase (liquibase.database.AbstractJdbcDatabase)1 InformixDatabase (liquibase.database.core.InformixDatabase)1 MSSQLDatabase (liquibase.database.core.MSSQLDatabase)1