4.1 Introduction
Database Application Continuous Integration and Continuous Delivery (CI/CD) represents a transformative approach in modern database management and application development. This methodology seamlessly integrates database changes into the development pipeline while ensuring swift and secure deployment to production environments. By combining the rapid feedback mechanisms of Continuous Integration with the streamlined deployment processes of Continuous Delivery, Database CI/CD addresses the critical need for agility and reliability in today's fast-paced software development landscape.

At its core, Database CI/CD aims to:
- Accelerate time-to-market for new features and updates.
- Maintain a consistently high quality of code and database schema.
- Facilitate immediate issue detection and resolution.
- Ensure that both application and database components are always in a deployable state.
This approach not only enhances development efficiency but also significantly improves the end-user experience through frequent, reliable releases.
The project
command in Oracle SQLcl is a powerful
tool designed to standardize database software versioning and create
releasable artifacts, including APEX elements. This command supports a
consistent model of development and operations, enabling repeatable builds
that can be applied in a specific order.
The key features of SQLcl Projects include:
- Versioned Feature Management: Allows database developers to identify individual changes and create versioned features or issues.
- Release Artifact Creation: Facilitates rolling multiple changes into a single release artifact that can be published for use.
- Ordered Installation: Ensures that artifacts are installable in the correct sequence, taking into account previously installed components in the environment.
- Source Control Integration: Supports creating branches in source control and exporting objects to these branches. Changes are realised using branch diffs and formatted consistently, allowing for easy identification through file checksums.
- Automated Release Packaging: When multiple branches or changes are created, SQLcl Projects can wrap them into a release, automatically setting up the execution of all changesets in the correct order on a target system.
The SQLcl Projects feature represents a significant step towards streamlining database development workflows, enhancing version control, and improving the overall efficiency of database application development and deployment processes.
4.1.1 Supported Objects
The supported database object types have been tested to work with
the project export
and project stage
commands. All types not included in the following list can be added using
the project stage add-custom
command with any custom SQL.
For more information about the add-custom
command, see
stage.
The database object types that are supported are:
- APEX_APP
- ORDS Modules
- CONSTRAINT
- CONTEXT
- FUNCTION
- INDEX
- MATERIALIZED_VIEW
- OBJECT_GRANT
- PACKAGE_BODY
- PACKAGE_SPEC
- PROCEDURE
- REF_CONSTRAINT
- ROLE_GRANT
- SEQUENCE
- SYNONYM
- SYSTEM_GRANT
- TABLE
- TRIGGER
- TYPE_BODY
- TYPE_SPEC
- USER
- VIEW
4.1.2 Database CI/CD Concepts
This section covers the concepts relevant to understanding SQLcl Projects.
- Database Continous Integration (CI) and Database Continuous Delivery (CD)
- Database Project Scaffolding
- Advanced Database Object Explorer
- Automated Database Migration Script Generation
- Building Releasable Artifacts
Database Continous Integration (CI) and Database Continuous Delivery (CD)
Database continuous integration (CI) is the process of frequently integrating database schema and logic changes into application development, aiming to provide immediate feedback on any issues. Database continuous delivery (CD) focuses on quickly and safely deploying those changes to production, ensuring that both application and database code are always in a deployable state. Together, database CI/CD reduces time-to-market and delivers consistent end-user value through frequent, high-quality releases.
Database Project Scaffolding
When initializing SQLcl Projects (using the project
init
command), SQLcl creates a set of files that
are used to manage the creation of files and changelogs.
There are three folders in the initial setup:
-
.dbtools
: This folder contains the following:- Project filters that are used by
project export
to filter out objects that will be exported. - Project format rules that are used to format the code when it is exported.
- Project configuration settings.
- Project filters that are used by
-
src
: This folder is where the exported objects from the database get placed. This is broken down by schema and objects types. -
dist
: The release artifacts are created in this folder. This folder gets populated by theproject stage
command and theproject release
command compresses its contents to create a release artifact.
──.dbtools
│ ├── filters
│ │ └── project.filters
│ ├── project.config.json
│ └── project.sqlformat.xml
├── dist
│ └── install.sql
└── src
│ └── database
├── .gitignore
└── README.md
Advanced Database Object Explorer
One of the main features of SQLcl Projects is exporting
database objects to the source control repository. The export
includes the SQL needed to create the object and a "snapshot" of the
object's metadata. The snapshot is stored as a comment and is
prefixed with sqlcl_snapshot
. It contains the
following information:
- Checksum calculated on the exported code
- Object type
- Object name
- Schema name
- SXML representation of the object as exported (see DBMS_METADATA SXML)
src/database/<SCHEMA>/<OBJECT_TYPE>/<OBJECT_NAME>[.sql|.pks|.pkb]
For
example, a table called doc
in the
HR
schema, the file path is
src/database/hr/tables/doc.sql
.
create table hr.doc (
id number
);
-- sqlcl_snapshot {"hash":"d992dceaa87a936e8242bdc81201f93030084ab5","type":"TABLE","name":"DOC","schemaName":"HR","sxml":"<TABLE xmlns=\"http://xmlns.abc.com/ku\" version=\"1.0\"> <SCHEMA>HR</SCHEMA> <NAME>DOC</NAME> <RELATIONAL_TABLE><COL_LIST> <COL_LIST_ITEM><NAME>ID</NAME><DATATYPE>NUMBER</DATATYPE> </COL_LIST_ITEM></COL_LIST><DEFAULT_COLLATION>USING_NLS_COMP</DEFAULT_COLLATION><PHYSICAL_PROPERTIES> <HEAP_TABLE></HEAP_TABLE></PHYSICAL_PROPERTIES> </RELATIONAL_TABLE></TABLE>"}
Automated Database Migration Script Generation
As developers add changes to the database, they are exported to the branch. Git branches are used to identify changes between units of work. Using the diffs between the branches, the changed files are identified.

Each object file has a specific checksum and, in some cases, specific SXML for the specification of the object. Using Oracle DBMS_METADATA_DIFF, the SXML files are compared and the DDL to make both objects the same is generated and added to the repository.
Building Releasable Artifacts
To ensure deployment consistency, statements need to run in a particular order. Once a statement is executed, it will never be run again (immutable). Liquibase support, which was introduced in SQLcl to allow users to create changesets for schemas or objects, can be run to install the objects into a database. SQLcl Projects builds on that support to generate changesets for each block of changes on a branch and build those together into a release.
This is achieved by taking the comparison set between branches and generating a hierarchy scaffolding of changelogs that represent the list of comparisons. Then, the comparison set is used to generate the SXML differences, which is then used to generate the DDL for the changes.

After an export of the changes, the project
stage
command generates the changesets for the
differences from the current branch and the main branch.
–-Example
next
├── release.changelog.xml
└── changes
└── initial-export
├── hr
│ └── table
│ └── doc.sql
└── stage.changelog.xml
project stage
command compares the contents of
the current branch and by default compares it against the base
branch of the project. The project configuration that controls the
base branch is git.defaultBranch
. This can be shown
by using the following
command:project config -list -name git.defaultBranch
To change it, use the same config
command with the
-set
option.
SQL> project config -list -name git.defaultBranch
+============================+
| SETTING NAME | VALUE |
+============================+
| git.defaultBranch | master |
+----------------------------+
project stage
command also has a
prescribed format. When the command is run, it creates the following
structure:next/changes/<branch_name>
In
the example, the branch created for the initial export of the
hr.doc
table is called
initial-export
. Each time a new branch is
created and the stage
command is run, there is a
new folder underneath
next/changes/<branch_name>
.
In this example, the doc.sql
file is
automatically prefixed with Liquibase-formatted SQL formats and is
defined as an individual changeset. doc.sql
will be
referenced in a changelog file.
-- Liquibase-formatted sql
-- changeset HR:d992dceaa87a936e8242bdc81201f93030084ab5 stripComments:false logicalFilePath:initial-export/hr/table/doc.sql
-- sqlcl_snapshot src/database/hr/tables/doc.sql:null:d992dceaa87a936e8242bdc81201f93030084ab5:create
create table hr.doc (
id number
);
In this example, the stage.changelog.xml references the Liquibase-formatted SQL changeset.
--stage.changelog.xml
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.3.xsd">
<!--BEGIN-->
<include file="hr/table/doc.sql" relativeToChangelogFile="true"/>
<!--END-->
<!--BEGIN CUSTOM-->
<!--END CUSTOM-->
</databaseChangeLog>
The next
folder has a
release.changelog.xml
right at the top.
This file is referenced again in the project
release
command.
--release.changelog.xml
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.3.xsd">
<!--BEGIN CHANGE-->
<include file="changes/initial-export/stage.changelog.xml" relativeToChangelogFile="true"/>
<!--END CHANGE-->
<!--BEGIN CODE-->
<!--END CODE-->
</databaseChangeLog>
When all the changes are created for a release, run the
project release
command to take all files
under the next
folder and move them to a "numbered
release" folder.
The final structure is:
releases
├── 1.0
│ ├── code
│ │ └── code.changelog.xml
│ ├── release.changelog.xml
│ └── changes
│ └── initial-export
│ ├── _custom
│ ├── hr
│ │ └── table
│ │ └── doc.sql
│ └── stage.changelog.xml
├── main.changelog.xml
├── next
│ └── release.changelog.xml
└── util
├── prechecks.sql
└── recompile.sql
To generate a re-runnable artifact, run the project
gen-artifact
command. This creates a file in
artifacts/<projectname>-<version>.zip
.
By default, artifacts are not included in the Git repository (To
change this, modify the .gitignore
file).
The contents of this artifact include:
releases/next/release.changelog.xml
releases/1.0/changes/initial-export/stage.changelog.xml
releases/1.0/changes/initial-export/hr/table/doc.sql
releases/1.0/release.changelog.xml
releases/1.0/code/code.changelog.xml
releases/util/recompile.sql
releases/util/prechecks.sql
releases/main.changelog.xml
README.md
install.sql
The last part is deploying the artifacts into a production
database. The command project deploy
takes the
generated artifact and runs it with SQLcl.
SQL> project deploy -file demo-1.0.zip
Starting the migration...
Running Changeset: initial-export/hr/table/doc.sql::d992dceaa87a936e8242bdc81201f93030084ab5::HR
Table HR.DOC created.
Liquibase: Update has been successful. Rows affected: 1
Installing/updating schemas
--Starting Liquibase at 2024-09-19T09:09:02.326601 (version [local build] #0 built at 2024-08-14 18:40+0000)
Table HR.DOC created.
UPDATE SUMMARY
Run: 1
Previously run: 0
Filtered out: 0
-------------------------------
Total change sets: 1
Produced logfile: sqlcl-lb-1726733337322.log
Operation completed successfully.
Migration has been completed.