Use native executors with DB2 z/OS
Liquibase supports the JCL native executor to integrate with DB2 z/OS mainframe operations into your Liquibase change management workflow. This allows you to submit JCL jobs and track them in the Liquibase changelog by using the runWith attribute on changesets in the changelog that require JCL jobs.
This article walks through how to add a column in the middle of a table on DB2 z/OS. Since DB2 does not support reordering columns directly, this operation requires unloading the table data, dropping the table, recreating it with the new column in the desired position, and then reloading the data.
While this specific example focuses on adding a column, the broader goal is to demonstrate how you can use Liquibase’s native JCL executor to automate any operation on DB2 z/OS that requires mainframe utilities, such as UNLOAD, LOAD, or any custom JCL-based task.
By following this pattern, you can adapt the same changelog structure to perform more complex or organization-specific workflows that integrate JCL jobs into your deployment pipeline.
1. Configure your liquibase.properties file
To use the JCL executor with Liquibase on DB2 z/OS, you must set several required properties in your liquibase.properties
file.
Before using the example properties file below, be sure to:
- Replace your_hostname, your_port, and your_db_name in the connection url for your database
- Replace your_username and your_password with the username and password for authenticating with the DB2 instance
- Replace your_schema with the name of the DB2 schema Liquibase should use.
- Set liquibase.jcl.create.logFile to true if you would like Liquibase to create additional logs for JCL. Logs are updated for each change type.
- Replace /logs with your preferred output file path. If you do not specify a
liquibase.jcl.logFilePath
, logs will be written to your system’s temporary folder. However, temporary folders are not intended for long-term storage and files stored there may be automatically removed by the operating system at any time. We recommend settingliquibase.jcl.logFilePath
to a specific, persistent location to ensure logs are retained as needed. - Set liquibase.jcl.overwrite.logFile to true if you would like Liquibase to create a new file for each changeset. In this implementation, each JCL has its own log file. Log files are named based on their changeset id and author. If you have the changeset
--changeset liquibase-deployer:unload-table-1 runWith:jcl
, the changset id will be unload-table-1. If the author is liquibase-developer, the output will be named unload-table-1-liquibase-deployer.log.
This property is set to false by default and will append the existing log file each time unless you set it to true.
Example liquibase.properties file
driver: com.ibm.db2.jcc.DB2Driver
url: jdbc:db2://your_hostname:your_port/your_db_name
username: your_username
password: your_password
defaultSchemaName: your_schema
liquibase.jcl.create.logFile: true
liquibase.jcl.logFilePath: /logs
liquibase.jcl.overwrite.logFile: true
liquibase.reports.enabled: true
2. Configure your changelog
The following changelog demonstrates how to add a column to the middle of a table on DB2 z/OS. Since DB2 doesn’t allow altering column order directly, you’ll need to:
- Unload the table's data
- Drop the table
- Create the table with the new column
- Load the table's data
Each of these steps is represented by a changeset in the changelog.
Before using this changelog, make sure you’ve created the appropriate .jcl
and .sql
files for each step and saved them in the correct paths referenced by --include
file.
Note: We support property substitution in your JCL scripts.
You can use these files if you'd like to use the example code:
Example Code
Note: While this example focuses on modifying table structure, the same pattern applies to any mainframe-native operation that you need to integrate into a Liquibase workflow. The runWith:jcl
option enables Liquibase to submit and manage these operations natively on DB2 z/OS, so be sure to add this to any changesets that include jcl.
--liquibase formatted sql
--changeset liquibase-deployer:unload-table-1 runWith:jcl
--comment: Unloading table data on DB2 z/OS
--file path:jcl/unload.jcl
--changeset liquibase-deployer:drop-objects-1
--comment Drop objects ahead of creation of table with new column in middle
--file path:sql/drop.sql
--changeset liquibase-deployer:create-alter-objects-1
--comment: Creating new table with new column in middle
--file path:sql/create-alter.sql
--changeset liquibase-deployer:load-table-1 runWith:jcl
--comment: Loading table data on DB2 z/OS
--file path:jcl/load.jcl
databaseChangeLog:
- changeSet:
id: unload-table-1
author: liquibase-deployer
runWith: jcl
changes:
- sqlFile:
path: jcl/unload.jcl
relativeToChangelogFile: true
- changeSet:
id: drop-objects-1
author: liquibase-deployer
changes:
- sqlFile:
path: sql/drop.sql
relativeToChangelogFile: true
- changeSet:
id: create-alter-objects-1
author: liquibase-deployer
changes:
- sqlFile:
path: sql/create-alter.sql
relativeToChangelogFile: true
- changeSet:
id: load-table-1
author: liquibase-deployer
runWith: jcl
changes:
- sqlFile:
path: jcl/load.jcl
relativeToChangelogFile: true
{
"databaseChangeLog": [
{
"changeSet": {
"id": "unload-table-1",
"author": "liquibase-deployer",
"runWith": "jcl",
"changes": [
{
"sqlFile": {
"path": "jcl/unload.jcl",
"relativeToChangelogFile": "true"
}
}
]
}
},
{
"changeSet": {
"id": "drop-objects-1",
"author": "liquibase-deployer",
"changes": [
{
"sqlFile": {
"path": "sql/drop.sql",
"relativeToChangelogFile": "true"
}
}
]
}
},
{
"changeSet": {
"id": "create-alter-objects-1",
"author": "liquibase-deployer",
"changes": [
{
"sqlFile": {
"path": "sql/create-alter.sql",
"relativeToChangelogFile": "true"
}
}
]
}
},
{
"changeSet": {
"id": "load-objects-1",
"author": "liquibase-deployer",
"runWith": "jcl",
"changes": [
{
"sqlFile": {
"path": "jcl/load.jcl",
"relativeToChangelogFile": "true"
}
}
]
}
}
]
}
<?xml version="1.0" encoding="UTF-8"?>
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/pro http://www.liquibase.org/xml/ns/pro/liquibase-pro-latest.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
<property name="PORT" value="6432"/>
<changeSet author="wesley (generated)" id="1692462595665-1" runWith="jcl">
<sqlFile path="jcl/unload.jcl"/>
</changeSet>
<changeSet author="wesley (generated)" id="1692462595665-2">
<sqlFile path="sql/drop.sql"/>
</changeSet>
<changeSet author="wesley (generated)" id="1692462595665-3">
<sqlFile path="sql/create-alter.sql"/>
</changeSet>
<changeSet author="wesley (generated)" id="1692462595665-4" runWith="jcl">
<sqlFile path="jcl/load.jcl"/>
</changeSet>
</databaseChangeLog>
3. Run the workflow
Once you have your liquibase.properties set up and your changelog file created, you run your changelog file to test that your operations complete successfully.
Use the example code to apply the changelog. Be sure to update your_changelog_file.
liquibase update --changelog-file=your_changelog_file
If the command is successful, you'll see:
Liquibase command 'update' was executed successfully
If there is an error you'll see:
JOB FAILED - JCL ERROR
You should also see a Liquibase update report open in a browser window with the execution details including the JCL script and generated SQL that you can use to troubleshoot any issues.