Pass a variable to an SQL changelog at runtime

Hi, I’m using the Liquibase Java API (version 4.25.1) for change management. In my SQL changelog, I have placeholders for specific properties, like so:

CREATE SCHEMA IF NOT EXISTS ${schemaName};

How can I, through the Liquibase Java API, make the desired properties available for use? Is this even possible? I have not seen any examples for the API, only for the CLI.

Below is my sample changelog and API code:

--liquibase formatted sql

--changeset dm:create-schema-sample context:"dev" failOnError:true
CREATE SCHEMA IF NOT EXISTS ${schemaName};
try (Connection connection = DriverManager.getConnection(url, user, password)) {
    Map<String, Object> config = new HashMap<>();
    Scope.child(config, () -> {
        JdbcConnection jdbcConnection = new JdbcConnection(connection);
        Database database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(jdbcConnection);
        CommandScope commandScope = new CommandScope(COMMAND_NAME);
        commandScope.addArgumentValue(CONTEXTS_ARG, "dev");
        commandScope.addArgumentValue(CHANGELOG_FILE_ARG, "/database/changelog.sql");
        commandScope.addArgumentValue(DATABASE_ARG, database);
        commandScope.execute();
    });
} catch (Exception e) {
    throw new RuntimeException(e);
}

I have tried a few different approaches, but the placeholder is never replaced by the actual property.

You can pass the property by specifying an environment variable or a Java system property.

This works for me:

java -DschemaName=NewSchema -classpath .:liquibase-core.jar:postgresql-42.0.6.jar TestClass

schemaName=MY_SCHEMA java -classpath .:liquibase-core.jar:postgresql-42.0.6.jar TestClass

Thanks for your answer. I’m running the code from AWS in a Lambda function, so unfortunately I can’t use your suggestions. Properties would have to be passed on in some other way, specifically through the Java API

Another option is to use the ChangeLogParameters object. You can create an instance of this type, set your property, and then pass it to the update command, like this:


        Scope.child(config, () -> {
            JdbcConnection jdbcConnection = new JdbcConnection(connection);
            Database database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(jdbcConnection);
            ChangeLogParameters changelogParameters = new ChangeLogParameters(database);
            changelogParameters.set("schemaName", "newSchema");
            CommandScope commandScope = new CommandScope(UpdateCommandStep.COMMAND_NAME);
            commandScope.addArgumentValue(UpdateCommandStep.CONTEXTS_ARG, "dev");
            commandScope.addArgumentValue(UpdateCommandStep.CHANGELOG_FILE_ARG, "/database/changelog.sql");
            commandScope.addArgumentValue(DbUrlConnectionArgumentsCommandStep.DATABASE_ARG, database);
            commandScope.addArgumentValue(DatabaseChangelogCommandStep.CHANGELOG_PARAMETERS, changelogParameters);**
            commandScope.execute();
        });
1 Like

@wwillard Amazing! That was exactly what I was looking for! :slight_smile: Thank you!