use of liquibase.database.AbstractJdbcDatabase in project keycloak by keycloak.
the class DefaultLiquibaseConnectionProvider method getLiquibase.
@Override
public Liquibase getLiquibase(Connection connection, String defaultSchema) throws LiquibaseException {
Database database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(new JdbcConnection(connection));
if (defaultSchema != null) {
database.setDefaultSchemaName(defaultSchema);
}
String changelog = LiquibaseJpaUpdaterProvider.CHANGELOG;
ResourceAccessor resourceAccessor = new ClassLoaderResourceAccessor(getClass().getClassLoader());
logger.debugf("Using changelog file %s and changelogTableName %s", changelog, database.getDatabaseChangeLogTableName());
((AbstractJdbcDatabase) database).set(INDEX_CREATION_THRESHOLD_PARAM, indexCreationThreshold);
return new Liquibase(changelog, resourceAccessor, database);
}
use of liquibase.database.AbstractJdbcDatabase 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
}
}
}
}
use of liquibase.database.AbstractJdbcDatabase in project liquibase by liquibase.
the class IndexSnapshotGenerator method addTo.
@Override
protected void addTo(DatabaseObject foundObject, DatabaseSnapshot snapshot) throws DatabaseException, InvalidExampleException {
if (!snapshot.getSnapshotControl().shouldInclude(Index.class)) {
return;
}
if (foundObject instanceof Table || foundObject instanceof View) {
if (foundObject instanceof View && !addToViews(snapshot.getDatabase())) {
return;
}
Relation relation = (Relation) foundObject;
Database database = snapshot.getDatabase();
Schema schema;
schema = relation.getSchema();
List<CachedRow> rs = null;
JdbcDatabaseSnapshot.CachingDatabaseMetaData databaseMetaData = null;
try {
databaseMetaData = ((JdbcDatabaseSnapshot) snapshot).getMetaDataFromCache();
rs = databaseMetaData.getIndexInfo(((AbstractJdbcDatabase) database).getJdbcCatalogName(schema), ((AbstractJdbcDatabase) database).getJdbcSchemaName(schema), relation.getName(), null);
Map<String, Index> foundIndexes = new HashMap<>();
for (CachedRow row : rs) {
String indexName = row.getString("INDEX_NAME");
if (indexName == null) {
continue;
}
if ((database instanceof AbstractDb2Database) && "SYSIBM".equals(row.getString("INDEX_QUALIFIER"))) {
continue;
}
Index index = foundIndexes.get(indexName);
if (index == null) {
index = new Index();
index.setName(indexName);
index.setRelation(relation);
short type = row.getShort("TYPE");
if (type == DatabaseMetaData.tableIndexClustered) {
index.setClustered(true);
} else if (database instanceof MSSQLDatabase) {
index.setClustered(false);
}
foundIndexes.put(indexName, index);
}
String ascOrDesc;
if (database instanceof Db2zDatabase) {
ascOrDesc = row.getString("ORDER");
} else {
ascOrDesc = row.getString("ASC_OR_DESC");
}
Boolean descending = "D".equals(ascOrDesc) ? Boolean.TRUE : ("A".equals(ascOrDesc) ? Boolean.FALSE : null);
index.addColumn(new Column(row.getString("COLUMN_NAME")).setComputed(false).setDescending(descending).setRelation(index.getRelation()));
}
// add clustered indexes first, than all others in case there is a clustered and non-clustered version of the same index. Prefer the clustered version
List<Index> stillToAdd = new ArrayList<>();
for (Index exampleIndex : foundIndexes.values()) {
if ((exampleIndex.getClustered() != null) && exampleIndex.getClustered()) {
relation.getIndexes().add(exampleIndex);
} else {
stillToAdd.add(exampleIndex);
}
}
for (Index exampleIndex : stillToAdd) {
boolean alreadyAddedSimilar = false;
for (Index index : relation.getIndexes()) {
if (DatabaseObjectComparatorFactory.getInstance().isSameObject(index, exampleIndex, null, database)) {
alreadyAddedSimilar = true;
}
}
if (!alreadyAddedSimilar) {
relation.getIndexes().add(exampleIndex);
}
}
} catch (Exception e) {
throw new DatabaseException(e);
}
}
if ((foundObject instanceof UniqueConstraint) && (((UniqueConstraint) foundObject).getBackingIndex() == null) && !(snapshot.getDatabase() instanceof DB2Database) && !(snapshot.getDatabase() instanceof DerbyDatabase)) {
Index exampleIndex = new Index().setRelation(((UniqueConstraint) foundObject).getRelation());
exampleIndex.getColumns().addAll(((UniqueConstraint) foundObject).getColumns());
((UniqueConstraint) foundObject).setBackingIndex(exampleIndex);
}
if ((foundObject instanceof ForeignKey) && (((ForeignKey) foundObject).getBackingIndex() == null)) {
Index exampleIndex = new Index().setRelation(((ForeignKey) foundObject).getForeignKeyTable());
exampleIndex.getColumns().addAll(((ForeignKey) foundObject).getForeignKeyColumns());
((ForeignKey) foundObject).setBackingIndex(exampleIndex);
}
}
use of liquibase.database.AbstractJdbcDatabase in project liquibase by liquibase.
the class PrimaryKeySnapshotGenerator method snapshotObject.
@Override
protected DatabaseObject snapshotObject(DatabaseObject example, DatabaseSnapshot snapshot) throws DatabaseException, InvalidExampleException {
Database database = snapshot.getDatabase();
Schema schema = example.getSchema();
String searchTableName = null;
if (((PrimaryKey) example).getTable() != null) {
searchTableName = ((PrimaryKey) example).getTable().getName();
searchTableName = database.correctObjectName(searchTableName, Table.class);
}
List<CachedRow> rs = null;
try {
JdbcDatabaseSnapshot.CachingDatabaseMetaData metaData = ((JdbcDatabaseSnapshot) snapshot).getMetaDataFromCache();
rs = metaData.getPrimaryKeys(((AbstractJdbcDatabase) database).getJdbcCatalogName(schema), ((AbstractJdbcDatabase) database).getJdbcSchemaName(schema), searchTableName);
PrimaryKey returnKey = null;
for (CachedRow row : rs) {
if ((example.getName() != null) && !example.getName().equalsIgnoreCase(row.getString("PK_NAME"))) {
continue;
}
String columnName = cleanNameFromDatabase(row.getString("COLUMN_NAME"), database);
short position = row.getShort("KEY_SEQ");
if (returnKey == null) {
returnKey = new PrimaryKey();
CatalogAndSchema tableSchema = ((AbstractJdbcDatabase) database).getSchemaFromJdbcInfo(row.getString("TABLE_CAT"), row.getString("TABLE_SCHEM"));
returnKey.setTable((Table) new Table().setName(row.getString("TABLE_NAME")).setSchema(new Schema(tableSchema.getCatalogName(), tableSchema.getSchemaName())));
returnKey.setName(row.getString("PK_NAME"));
}
String ascOrDesc = row.getString("ASC_OR_DESC");
Boolean descending = "D".equals(ascOrDesc) ? Boolean.TRUE : "A".equals(ascOrDesc) ? Boolean.FALSE : null;
boolean computed = false;
if (descending != null && descending) {
computed = true;
}
returnKey.addColumn(position - 1, new Column(columnName).setDescending(descending).setComputed(computed).setRelation(((PrimaryKey) example).getTable()));
setValidateOptionIfAvailable(database, returnKey, row);
}
if (returnKey != null) {
Index exampleIndex = new Index().setRelation(returnKey.getTable());
exampleIndex.setColumns(returnKey.getColumns());
returnKey.setBackingIndex(exampleIndex);
}
return returnKey;
} catch (SQLException e) {
throw new DatabaseException(e);
}
}
use of liquibase.database.AbstractJdbcDatabase in project liquibase by liquibase.
the class PrimaryKeySnapshotGenerator method addTo.
@Override
protected void addTo(DatabaseObject foundObject, DatabaseSnapshot snapshot) throws DatabaseException {
if (!snapshot.getSnapshotControl().shouldInclude(PrimaryKey.class)) {
return;
}
if (foundObject instanceof Table) {
Table table = (Table) foundObject;
Database database = snapshot.getDatabase();
Schema schema = table.getSchema();
List<CachedRow> rs = null;
try {
JdbcDatabaseSnapshot.CachingDatabaseMetaData metaData = ((JdbcDatabaseSnapshot) snapshot).getMetaDataFromCache();
rs = metaData.getPrimaryKeys(((AbstractJdbcDatabase) database).getJdbcCatalogName(schema), ((AbstractJdbcDatabase) database).getJdbcSchemaName(schema), table.getName());
if (!rs.isEmpty()) {
PrimaryKey primaryKey = new PrimaryKey().setName(rs.get(0).getString("PK_NAME"));
primaryKey.setTable((Table) foundObject);
if (!database.isSystemObject(primaryKey)) {
table.setPrimaryKey(primaryKey.setTable(table));
}
}
} catch (SQLException e) {
throw new DatabaseException(e);
}
}
}
Aggregations