liquibase.sqlgenerator.SqlGenerator
Overview
liquibase.statement.SqlStatement
implementations define specific operations that can be run against a database, such as
liquibase.statement.core.CreateTableStatement
or liquibase.statement.core.DropColumnStatement
.
However, they only define the what, not the how.
It is the job of the liquibase.sqlgenerator.SqlGenerator
implementations to know how to actually apply those operations to a given database.
For more information on change types, see the change types guide.
Tip
The default SqlGenerators for a given SqlStatement are named by replacing "Statement" from the class name with "Generator". For example, CreateTableStatement -> CreateTableGenerator.
For SqlGenerators that handle specific environments, append a description of what makes it different to the end. For example, CreateTableStatement -> CreateTableGeneratorOracle
sequenceDiagram
Calling Code->>SqlStatement: Creates and configures
Calling Code->>Executor: execute(sqlStatement)
Executor->>SqlGeneratorFactory: generateSql(sqlStatement)
SqlGeneratorFactory->>SqlGenerator: generateSql
SqlGenerator-->>SqlGeneratorFactory: returns SQL
SqlGeneratorFactory-->>Executor: returns SQL
Executor->>Database: Executes SQL
Example
When parsing a changelog file, Liquibase creates a CreateTableChange
object with the table name and column definitions specified in the changelog file.
When the Change is "executed", it will first create a CreateTableStatement
object containing the same information and pass that to the Executor
.
Liquibase will then go through all the available SqlGenerators
that say they support CreateTableStatement
for the given database and use the one with the
highest priority to come up with the actual SQL to run to create the table.
SqlGenerator Selection
Each SqlGenerator
has a supports()
method which the SqlGeneratorFactory
uses to determine which implementations can apply the SqlStatement to the given database.
Of all the supported SqlGenerator
implementations, Liquibase will use the one with the highest priority.
This allows extensions to either define a default logic for a SqlStatement OR override other SqlGenerators with a better implementation.
API Highlights
Auto-Registration
SqlGenerators are dynamically discovered, so must have a no-arg constructor and be registered in META-INF/services/liquibase.sqlgenerator.SqlGenerator
.
supports()
This method returns whether your SqlGenerator can be used for the given SqlStatement and Database.
Since SqlGenerator classes should be defined using generics, Liquibase will know the SqlStatement types it supports and not have to rely on supports() unless the SqlStatement class is correct. Therefore, this method generally only has to check that the Database object is correct.
Warning
Do not return true
for SqlStatements the generator logic cannot handle.
The default implementation from AbstractSqlGenerator
returns true
, which is too broad for database-specific SqlGenerator implementations
getPriority()
Used in selecting the instance to use.
Returns PRIORITY_DEFAULT
for default/standard logic and PRIORITY_DATABASE
for logic unique to a database.
generateSql()
Returns the SQL to run as liquibase.sql.Sql objects.
The normal class to use for those Sql
objects is liquibase.sql.UnparsedSql
.
This function returns an array of Sql statements because a single logical statement may require multiple SQL statements in some databases.
For example, AddPrimaryKeyGenerator
may return a SQL call to add the primary key and another to reorganize the table.
Tip
When extending an existing SqlGenerator
Depending on how different the SQL is from the base class, either call super.generateSql()
and modify the objects in the array before returning it,
OR create a new Sql[]
array from scratch.
It tends to be best to modify the SQL generated by the parent class because that avoids having to duplicate all the
argument-handling logic that already exists in the parent. One approach is to call super.generateSql()
and then create new UnparsedSql
objects
using the originalSql.toSql()
value passed through string replacements.
Warning
When creating SQL, object names must be wrapped in database.escape...()
calls to correctly handle reserved words
and database.escapeStringForDatabase()
around any string literals to handle special characters like '
inside them.
validate() and warn()
Called to validate whether the SqlStatement is correctly configured. For example, if the logic requires the tableName to be set, add that to the ValidationErrors
that are returned.
This method is always called, so you do not need to re-validate configuration in other methods.
If there is no additional validation to perform vs. the base logic there is no need to override this method.
The warn(SqlStatement, Database, SqlGeneratorChange)
method is similar to validate but returns non-fatal warnings to the user.
Tip
When extending a base SqlGenerator, call super.validate(statement, database, sqlGeneratorChain)
as a starting point,
then perform extra checks (or remove not-actually-invalid errors) before returning the ValidationErrors.
generateStatementsIsVolatile()
Ideally, the logic should be able to generate the correct SQL purely from the information stored in the SqlStatement object.
However, if that information is incomplete and the logic must check the database state or other external environments, this method must true
to mark it as "volatile".
That lets Liquibase know that for commands such updateSql
, it cannot rely on what your generateSql
method returns because the internal database state is not actually correct.
API Details
The complete javadocs for liquibase.sqlgenerator.SqlGenerator
is available at https://javadocs.liquibase.com
Extension Guides
The following guides provide relevant examples: