I am trying to use PreConditions to skip CreateTable changesets and MARK_RAN if the table exists. The PreConditions seem to be ignored as Liquibase logs a table exists error as it tries to create an existing table in MYSQL (in AWS Aurora).
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.10.xsd">
<changeSet author="xxx" id="1231">
<preConditions onFail="MARK_RAN">
<not>
<tableExists tableName="tenant"/>
</not>
</preConditions>
<createTable tableName="tenant">
<column autoIncrement="true" name="tenantId" type="int(11)">
<constraints primaryKey="true" nullable="false"
unique="false" />
</column>
...
The changelogs are being applied using the following Java code:
final Database dataBase = DatabaseFactory.getInstance()
.findCorrectDatabaseImplementation(new JdbcConnection(connection));
final Liquibase liquiBase = new liquibase.Liquibase(CHANGE_LOG_FILE, RESOURCE_ACCESSOR, dataBase);
liquiBase.update(CONTEXTS, LABEL_EXPRESSION);
The error:
Response: {"errorMessage":"Failed to connect to Liquibase due to Migration failed for change set db.changelog-1.1.xml::1231::xxx:\n Reason: liquibase.exception.DatabaseException: Table 'tenant' already exists [Failed SQL: CREATE TABLE xxx_global.tenant (tenantId INT AUTO_INCREMENT NOT NULL, region VARCHAR(255) NULL, tenantName VARCHAR(255) NULL, CONSTRAINT PK_TENANT PRIMARY KEY (tenantId))]"}
My only other thought would be to try to use a custom context on the CreateTable changesets for clean installations
I found the issue and it was not a Liquibase issue. The AWS stack containing the Lambda code to call Liquibase was not updating. I deleted and recreated the stack and it started working.
Related
I want to use liquibase on spring boot startup to add a new column to an existing table.
But in the particular schema I'm working with I have already added it manually.
So I get an error on startup indicating the column already exists (because it does):
Error creating bean with name 'liquibase' defined in class path resource
[...LiquibaseConfiguration.class]: LiquibaseException:
liquibase.exception.MigrationFailedException: Migration failed for
change set db/changelog/AlterTable_MyTable_AddNewCol.xml::1::me (manual):
Reason: liquibase.exception.DatabaseException:
Duplicate column name 'new_col' [Failed SQL: (1060)
ALTER TABLE my_schema.my_table ADD new_col INT NULL]
Is there a liquibase precondition option to add the column only if it does not already exist; and if it already exists, don't error out?
I tried the following and the process errors out with the above error:
<databaseChangeLog ... >
<changeSet author="me (manual)" id="1">
<preConditions onFail="WARN">
<columnExists tableName="my_table" columnName="new_col" />
</preConditions>
<addColumn tableName="my_table">
<column name="new_col" type="integer"/>
</addColumn>
</changeSet>
</databaseChangeLog>
I'm currently assigning a predefined property value to some tag's attributes, but I want to use it as well inside xml tags. Below is an example based on Liquibase documentation:
<?xml version="1.0" encoding="utf-8"?>
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.4.xsd"
>
<property name="schema.name" value="DBPECG"/>
<changeSet id="20201005103200-1" author="felipe.rudolfe" objectQuotingStrategy="QUOTE_ALL_OBJECTS">
<createTable schemaName="${schema.name}" tableName="TB_IES" >
.
.
.
</createTable>
</changeSet>
</databaseChangeLog>
And here is what I want to do. I want to use schema.name in a way such as this, inside sql tag:
<changeSet id="20201005103200-3" author="felipe.rudolfe" objectQuotingStrategy="LEGACY">
<sql>
ALTER TABLE ${schema.name}.TB_IES ADD CONSTRAINT...
</sql>
</changeSet>
Is there a way to do this?
Liquibase allows dynamic substitution of properties in changelog files. We can configure multiple properties inside a file and then use them wherever required. In your case, we can just configure property "schemaName" with some value and then use it in changelog file using ${schemaName} syntax.
Liquibase assigns or prioritizes value for configured property in below order:
As an attribute passed to your liquibase runner.
As a JVM sytem property
As an environment variable
As a CLI attribute if you are running liquibase through command line
In liquibase.properties file
In the parameters block (property element of the DATABASECHANGELOG
table)
You can do it as below example code snippet:
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.8.xsd">
<changeSet author="author" id="some-unique-id" context="some-context">
**Your SQL query/ transactional logic goes here**
<sql>
ALTER TABLE ${schemaName}.TB_IES ADD CONSTRAINT...
</sql>
</changeSet>
</databaseChangeLog>
In liquibase.properties file, I will configure this property as follows:
schemaName=DBPECG
Note: above example is using 1 property (schemaName). You can use only even more than that.
If you need help with creating "liquibase.properties" file, visit this link
Cheers!
my project is trying to migrate to liquibase but the lack of support for bind variables is making this difficult.
During our deployment we have sql scripts containing sqlplus substitution variables, like for example.
-- load_seed.sql ---
insert into <table>
values('&&host', '&&port', '&&user');
The value of these variables is different per environment, therefore we define profiles like these.
<DEV_profile.sql>
DEFINE host='dev.company.org'
DEFINE port=4008
..
<UAT_profile.sql>
DEFINE host='uat.company.org'
...
and the we run the deployment like this:
./deploy.ksh DEV
---- deploy.ksh ---
sqlplus <<END
<connection>
#$1_profile
#load_seed
The correct profile is picked up at execution time and the variables replaced.
Could you please suggest how to handle a case like this with Liquibase?
The equivalent functionality in Liquibase is provided by changelog parameters.
In your changelog, you define parameters, which are basically key-value pairs, and liquibase decides which value to use based on the value of a context or a label or a dbms.
When you want to apply the changeset to a given environment, you specify the context or label on the command line or in the liquibase.properties. Liquibase can determine the dbms based on the connection URL.
Here's an example that is somewhat similar to what you describe:
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.6.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd">
<property name="host" value="dev.company.org" context="DEV"/>
<property name="port" value="4008" context="DEV"/>
<property name="user" value="DEV_USER" context="DEV"/>
<property name="host" value="uat.company.org" context="UAT"/>
<property name="port" value="4321" context="UAT"/>
<property name="user" value="UAT_USER" context="UAT"/>
<changeSet id="1" author="joe">
<insert tableName="someTableName">
<column name="host" type="varchar(255)" value="${host}"/>
<column name="port" type="varchar(8)" value="${port}"/>
<column name="user" type="varchar(255)" value="${user}"/>
</insert>
</changeSet>
</databaseChangeLog>
https://docs.liquibase.com/concepts/basic/changelog-property-substitution.html
does not support sql changelog property substitution. you would have to migrate to (xml, yaml, json)
The Problem
I recently upgraded Liquibase to 3.6.2 from 3.4.2.
Loading seed data from a CSV into text fields now results in a CLOB resource error. Before it would simply insert the text as a value.
The Setup
I'm using Liquibase to manage migrations of my data.
I have a table with an code and description column. description is of type TEXT.
<changeSet author="" id="create-table-degrees">
<createTable tableName="degrees">
<column name="code"
type="varchar(2)">
<constraints primaryKey="true"/>
</column>
<column name="description"
type="text">
<constraints unique="true"/>
</column>
</createTable>
<rollback>
<dropTable tableName="degrees"/>
</rollback>
</changeSet>
I have seed data in a CSV:
code,description
"D1","MASTERS"
"D2","DOCTORATE"
I load it using loadData:
<changeSet author="" id="seed-degrees">
<loadData file="seeds/degrees.csv"
tableName="degrees" />
</changeSet>
The Error
Unexpected error running Liquibase: CLOB resource not found: MASTERS
The Question
Is there a way to keep Liquibase from interpreting seed values as file paths instead of strings, or do I need to manually define the column types as String in loadData.
e.g. I would like to avoid having to modify the old changeSet to:
<changeSet author="" id="seed-degrees">
<loadData file="seeds/degrees.csv"
tableName="roles">
<column name="description" type="string" />
</loadData>
</changeSet>
The workaround listed in CORE-3287: Anver S December 3, 2018, 3:07 PM
While adding an explicit column type definition as defined in original
stackoverflow post
<column name="description" type="string" />
does the trick - for me it effectively requires to update already
applied changesets which ideally I'd try to avoid.
I'm trying to execute one of two insert depending on value of Java system property 'type'. But when I run updateSQL, it generates both insert entries despite of value and even presence of variable 'type'. This are my change sets:
<!--changes for CS only-->
<changeSet id="57" author="mborodin">
<preConditions onFail="CONTINUE">
<changeLogPropertyDefined property="type" value="cs"/>
</preConditions>
<sql>INSERT INTO cs.config(key, value) values('autochecks.findProfilesInDb', 'true'),
('web.type', 'cs'), ('globalFailureDetectorEnabled', 'true');
</sql>
</changeSet>
<!--changes for LCS only-->
<changeSet id="58" author="mborodin">
<preConditions onFail="CONTINUE">
<and>
<changeLogPropertyDefined property="type" value="lcs"/>
<not>
<changeSetExecuted
id="57"
author="mborodin"
changeLogFile="${changeLogFile}"
/>
</not>
</and>
</preConditions>
<sql>INSERT INTO cs.config(key, value) values('autochecks.findProfilesInDb', 'false'),
('web.type', 'local-cs'), ('globalFailureDetectorEnabled', 'false');
</sql>
</changeSet>
What's wrong? Is there a better way to work it?
From the first nvoxland's answer at liquibase forum:
… we added the onSqlOutput tag. IGNORE just assumes that the
precondition cannot be applied, but you should continue to "run" the
changeLog. TEST means to actually run the precondition. FAIL mean to
stop the execution of the changelog, alerting you that you cannot run
in updateSql mode because a correct execution of the precondtion is
required to continue.
The problem has been solved by adding onSqlOutput="TEST" tag.