> For the complete documentation index, see [llms.txt](https://developer.harness.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://developer.harness.io/database-devops/use-db-devops/changelogs-and-schema-changes/liquibase/oracle-native-executor.md).

# Oracle Changesets & Native Command Execution

A practical reference for running Oracle schema and PL/SQL changes using native SQL\*Plus/SQLcl execution in Harness Database DevOps. Includes changesets, configuration, supported commands, and a full

A practical reference for running Oracle schema and PL/SQL changes using native SQL\*Plus/SQLcl execution in Harness Database DevOps. Includes changesets, configuration, supported commands, and a full changelog example.

This guide covers the Oracle Native Executor: how to opt in per changeset, which settings you can configure, and what is and is not supported. Go to [Build a Changelog File](/database-devops/use-db-devops/changelogs-and-schema-changes/liquibase/build-a-changelog.md) for general changelog authoring guidance, and go to [Get started with changelogs](/database-devops/setup-db-devops/get-started-with-changelogs.md) to review a full changelog example.

### What is native execution for Oracle?

Oracle schema changes often depend on tooling and behavior that only SQL\*Plus/SQLcl provide, not plain JDBC. Migration scripts already written for Oracle commonly rely on:

* PL/SQL blocks, packages, and triggers
* `SET` and `SPOOL` commands
* `@` script includes

Beyond syntax, these scripts often also encode years of DBA-specific automation and deployment conventions built around a SQL*Plus/SQLcl environment. The Oracle Native Executor runs these scripts through an actual SQL*Plus/SQLcl process instead of JDBC. Key characteristics:

* No JDBC abstraction layer for native changesets.
* SQL\*Plus/SQLcl-aware execution model.
* Compatible with PL/SQL, packages, triggers, SET/SPOOL, and @ includes, subject to the injected preamble below.

{% hint style="info" %}
Harness injects its own preamble around your script: `SET DEFINE OFF`, `SET ECHO ON`, `SET SQLBLANKLINES ON`, plus its own `SPOOL`/`EXIT` handling. `SET DEFINE OFF` means substitution variables (`&var`) are **not** processed; they run as literal text, not resolved values. If your script uses `@` includes or its own `SPOOL` commands, review how they interact with this injected preamble before relying on them.
{% endhint %}

SQLcl is bundled in the Oracle image, so there is no extra setup for the common case.

### How do I run existing SQL\*Plus scripts in a pipeline?

Native execution is opt-in **per changeset**, not a pipeline-wide setting. Harness Database DevOps supports multiple ways to mark a changeset for native execution; all integrate seamlessly into the standard pipeline execution flow, approvals, and governance model.

{% hint style="info" %}
At a high level: a developer or DBA commits a migration → Liquibase tracks the changeset → the Oracle Native Executor runs the selected changesets through SQL\*Plus/SQLcl → migration status and history remain part of the Liquibase workflow.
{% endhint %}

{% tabs %}
{% tab title="YAML Changeset" %}
In this approach, you add `runWith: sqlplus` as a key on the changeset in a YAML changelog. This method is well-suited for:

* Changelogs already authored in Liquibase YAML
* Teams that prefer YAML's compact, nested syntax over XML

```yaml
databaseChangeLog:
  - changeSet:
      id: 1
      author: dba
      runWith: sqlplus
      runInTransaction: false
      changes:
        - sqlFile:
            path: complex_plsql.sql
```

**How it works:**

* You add `runWith: sqlplus` as a key on the changeset.
* The referenced SQL file is executed against the target Oracle instance through SQL\*Plus/SQLcl, not JDBC.
* Only this changeset is affected. Everything else in the changelog keeps running over JDBC.

This syntax is a common fit for PL/SQL packages, triggers, and other scripts that depend on SQL\*Plus/SQLcl behavior, for example, referencing an `oracle/create_order_package.sql` file the same way.
{% endtab %}

{% tab title="SQL Formatted Changelog" %}
In this approach, the changeset and its `runWith` flag are declared as comment headers directly inside a `.sql` file. This method is well-suited for:

* Teams that already maintain changelogs as plain SQL files
* Migrations with several related PL/SQL changesets in one file

```sql
--liquibase formatted sql
--changeset dba:1 runWith:sqlplus runInTransaction:false
CREATE TABLE ...;
```

**How it works:**

* You add `runWith:sqlplus` to the changeset's comment header.
* Liquibase parses the file's comment headers to identify changeset boundaries.
* At runtime, each `runWith:sqlplus` block is handed to SQL\*Plus/SQLcl for execution.

This pattern keeps large, multi-statement PL/SQL scripts in a single reviewable file.
{% endtab %}

{% tab title="XML Changeset" %}
In this approach, you add `runWith="sqlplus"` directly to the `<changeSet>` tag. This method is well-suited for:

{% hint style="info" %}
Most customers and internal teams use the YAML changeset format instead — see the YAML Changeset tab. Use XML mainly if you are already maintaining an XML-based changelog.
{% endhint %}

* Changelogs already authored in Liquibase XML

```xml
<changeSet id="1" author="dba" runWith="sqlplus" runInTransaction="false">
  <sqlFile path="complex_plsql.sql"/>
</changeSet>
```

**How it works:**

* You add `runWith="sqlplus"` as an attribute on the changeset.
* SQL\*Plus/SQLcl executes the referenced SQL file against your target Oracle instance, not JDBC.
* Only this changeset is affected — everything else in the changelog keeps running over JDBC.

This approach keeps existing DBA-authored `.sql` files untouched.
{% endtab %}
{% endtabs %}

![Oracle native executor flow from changeset to SQL\*Plus/SQLcl](https://4089095456-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FWwqb27iUU3gZvwGNroZp%2Fuploads%2Fgit-blob-bff1dabfcfe91ae2b8aa55068779d2e036c597c8%2Foracle-native-exec.png?alt=media)

### Supported commands and caveats

The Oracle Native Executor works alongside standard Liquibase commands and database change management. It does not replace them.

{% hint style="warning" %}
**Preview commands (updateSql / rollbackSql)**

For `runWith="sqlplus"` changesets, `updateSql` and `rollbackSql` do not execute anything. The `LoggingExecutor` renders the raw script only. Treat the output as informational, not a validated dry run.
{% endhint %}

{% hint style="info" %}
**Transaction handling**

`runInTransaction="true"` is rejected by Liquibase when combined with `runWith="sqlplus"`. Set `runInTransaction="false"` on every native changeset, and handle commit/rollback logic inside the script itself.
{% endhint %}

### Configuration

Set executor options in the pipeline step's `globalSettings` (or `settings:` for DB Command/Liquibase Command). Setting names use kebab-case; there are no Harness-proprietary names.

```yaml
type: DBSchemaApply
spec:
  connectorRef: my-registry-connector
  migrationType: Liquibase
  dbSchema: myschema
  dbInstance: prod
  globalSettings:
    sqlplus-path: /opt/oracle/sqlcl/bin/sql
    sqlplus-timeout: "3600"
    sqlplus-username: "sys as sysdba"   # only when auth differs from the DB instance
    sqlplus-password: <+secrets.getValue("oracle_sys_pw")>
```

### Settings reference

All settings are optional.

| **Setting**                             | **Default**                                              | **Description**                                                                                                 |
| --------------------------------------- | -------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
| `sqlplus-path`                          | `/opt/oracle/sqlcl/bin/sql` (bundled)                    | Path to the SQL\*Plus/SQLcl executable.                                                                         |
| `sqlplus-sqlerror`                      | `WHENEVER SQLERROR EXIT FAILURE`                         | Behavior on statement error.                                                                                    |
| `sqlplus-timeout`                       | `1800`                                                   | Max seconds the process may run before timing out.                                                              |
| `sqlplus-args`                          | —                                                        | Extra raw arguments passed to SQL\*Plus/SQLcl.                                                                  |
| `sqlplus-username` / `sqlplus-password` | Falls back to the DB instance JDBC connector credentials | Override login when you need different auth than the DB instance JDBC connector (for example, `sys as sysdba`). |
| `sqlplus-create-spool`                  | `true`                                                   | Auto-create a spool (output log) file.                                                                          |
| `sqlplus-keep-temp`                     | `false`                                                  | Keep the generated temp script file after execution.                                                            |
| `sqlplus-keep-temp-path`                | System temp                                              | Where to keep the temp file, if kept.                                                                           |
| `sqlplus-keep-temp-name`                | Auto-generated                                           | Name of the kept temp file.                                                                                     |
| `sqlplus-keep-temp-overwrite`           | `true`                                                   | Whether a re-run overwrites the same temp file name.                                                            |

**Complete example changelog**

A full example demonstrating table creation, a stored procedure, procedure invocation, and a query, all run natively.

```yaml
databaseChangeLog:
  - changeSet:
      id: oracle-native-demo-1
      author: cb-demo
      runWith: sqlplus
      runInTransaction: false
      changes:
        - sql:
            sql: |
              CREATE TABLE CB_ORACLE_DEMO (
                ID NUMBER PRIMARY KEY,
                NAME VARCHAR2(100)
              );
  - changeSet:
      id: oracle-native-demo-2
      author: cb-demo
      runWith: sqlplus
      runInTransaction: false
      changes:
        - sql:
            sql: |
              CREATE OR REPLACE PROCEDURE CB_DEMO_SEED_AND_AUDIT AS
                v_cnt NUMBER;
              BEGIN
                INSERT INTO CB_ORACLE_DEMO VALUES (1, 'Alice');
                INSERT INTO CB_ORACLE_DEMO VALUES (2, 'Bob');
                SELECT COUNT(*) INTO v_cnt FROM CB_ORACLE_DEMO;
                IF v_cnt != 2 THEN
                  RAISE_APPLICATION_ERROR(-20001, 'seed row count mismatch');
                END IF;
                COMMIT;
              END;
              /
  - changeSet:
      id: oracle-native-demo-3
      author: cb-demo
      runWith: sqlplus
      runInTransaction: false
      changes:
        - sql:
            sql: |
              BEGIN
                CB_DEMO_SEED_AND_AUDIT;
              END;
              /
  - changeSet:
      id: oracle-native-demo-4
      author: cb-demo
      runWith: sqlplus
      runInTransaction: false
      changes:
        - sql:
            sql: |
              SELECT * FROM CB_ORACLE_DEMO;
```

### Best practices and usage notes

1. **Transactions:** Native changesets cannot be wrapped in a Liquibase-managed transaction. Always set `runInTransaction="false"` and handle commit/rollback logic inside the script itself.
2. **Previews are not dry runs:** Do not rely on `updateSql`/`rollbackSql` to validate native changesets before applying them. The output is the raw script, not a simulation of what SQL\*Plus will do.
3. **Elevated credentials:** Only set `sqlplus-username`/`sqlplus-password` when you need different auth than the DB instance JDBC connector (for example, `sys as sysdba`). Otherwise, leave credentials on the DB instance so access stays centrally managed.
4. **Debugging with temp files:** Turn on `sqlplus-keep-temp` (and optionally set `sqlplus-keep-temp-path`/`-name`) when you need to inspect exactly what was sent to SQL\*Plus, then turn it back off for normal runs.
5. **Mixed changelogs:** It is expected, and often the point, to have a changelog with some changesets on `runWith="sqlplus"` and others on plain JDBC. Only tag what genuinely needs native behavior.

### Choose the right approach

The choice between the YAML changeset, the XML changeset, and the SQL formatted changelog depends on your team's workflow and how your existing scripts are already organized. YAML is the most common choice for both customers and internal teams.

| **Use Case**                                   | **Recommended Approach**                       |
| ---------------------------------------------- | ---------------------------------------------- |
| Changelog already authored in Liquibase YAML   | YAML changeset with `runWith: sqlplus`         |
| Changelog already authored in Liquibase XML    | XML changeset with `runWith="sqlplus"`         |
| Existing DBA-authored `.sql` scripts           | SQL formatted changelog with `runWith:sqlplus` |
| Multiple related PL/SQL changesets in one file | SQL formatted changelog                        |
| Referencing a single external script           | YAML or XML changeset with `sqlFile`           |

### Conclusion

Native execution does not mean giving up the database change management capabilities you are already using. Native changesets stay part of the same Liquibase changelog, are tracked the same way, and support the same core commands (**update**, **rollback**, **rollbackCount**, **status**, **history**, **tag**), with `runInTransaction="false"` required and preview commands treated as informational only.

As a result, Oracle migrations follow the same governed Database DevOps lifecycle as other database workloads in Harness Database DevOps, letting teams bring proven SQL\*Plus/SQLcl scripts into automated pipelines without rewriting them for JDBC.

### Next steps

* Go to [Build a Changelog File](/database-devops/use-db-devops/changelogs-and-schema-changes/liquibase/build-a-changelog.md) to structure a changelog mixing JDBC and native Oracle changesets.
* Go to [Get started with changelogs](/database-devops/setup-db-devops/get-started-with-changelogs.md) to review a full end-to-end changelog example.
* Go to [Automatic and custom rollbacks](/database-devops/use-db-devops/deployment-pipeline-configuration/rollback-and-failure-strategies/automatic-and-custom-rollback.md) to configure rollback strategies for your Oracle changesets.

{% @harness-feedback/feedback %}
