> ## Documentation Index
> Fetch the complete documentation index at: https://docs.bytebase.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Transaction Mode

<Note>
  Supported databases: MySQL, PostgreSQL, Oracle, SQL Server, TiDB, Redshift
</Note>

Bytebase provides transaction management to ensure safe database changes. By default, all SQL statements are wrapped in a transaction, allowing automatic rollback if errors occur during execution.

## Enable/Disable Transactions

When creating a plan, you can control whether SQL statements are executed within a transaction:

* **Enabled (default)**: All statements run within a transaction boundary for safety
* **Disabled**: Statements execute directly without transaction wrapping

<img src="https://mintcdn.com/dbx/4AsGK5ww-0OxBwwy/content/docs/change-database/transaction-mode/bb-transaction-mode.webp?fit=max&auto=format&n=4AsGK5ww-0OxBwwy&q=85&s=560b3c81703b310fa76f6306fbfee186" alt="Transaction mode toggle" width="2024" height="666" data-path="content/docs/change-database/transaction-mode/bb-transaction-mode.webp" />

## Statements that cannot run in a transaction block

Some PostgreSQL statements cannot run inside an **explicit transaction block**. When such a statement runs after `BEGIN` — or when a migration tool wraps it in a transaction — PostgreSQL rejects it with an error like:

```
ERROR: CREATE INDEX CONCURRENTLY cannot run inside a transaction block
```

For example, this fails because the statement is wrapped in an explicit transaction block:

```sql theme={null}
BEGIN;
CREATE INDEX CONCURRENTLY idx_users_email ON users(email);
COMMIT;
```

While the same statement runs fine on its own under autocommit (no `BEGIN`):

```sql theme={null}
CREATE INDEX CONCURRENTLY idx_users_email ON users(email);
```

<Info>
  You do not need to run these statements manually outside Bytebase. Bytebase automatically detects the most common ones and runs them outside the transaction — even in the default, transaction-enabled mode:

  * `CREATE INDEX CONCURRENTLY`
  * `DROP INDEX CONCURRENTLY`
  * `VACUUM`
  * `DROP DATABASE`

  Include them in your change like any other statement. Bytebase executes them after the surrounding transaction commits, so they never hit the error above — the plan UI notes this as "Non-transactional statements will be executed last."
</Info>

For other statements that cannot run in a transaction block but are **not** auto-detected — such as `REINDEX ... CONCURRENTLY`, `CREATE DATABASE`, `CREATE TABLESPACE`, `ALTER SYSTEM`, and some `ALTER TYPE ... ADD VALUE` cases — disable transaction mode so Bytebase runs the statements directly:

```sql theme={null}
-- txn-mode = off

REINDEX INDEX CONCURRENTLY idx_users_email;
```

<Warning>
  `-- txn-mode = off` disables transaction wrapping for the **entire** script — if a later statement fails, earlier ones stay committed. Put a non-transactional statement in its own migration so the rest of your changes keep transactional, all-or-nothing safety.
</Warning>

## MySQL-Specific Settings

For MySQL databases, Bytebase offers additional control over transaction isolation levels to manage concurrent access and data consistency:

<img src="https://mintcdn.com/dbx/4AsGK5ww-0OxBwwy/content/docs/change-database/transaction-mode/bb-transaction-mode-mysql.webp?fit=max&auto=format&n=4AsGK5ww-0OxBwwy&q=85&s=db3c4b0a66e006e064e283a639ccdc68" alt="MySQL transaction isolation levels" width="1830" height="486" data-path="content/docs/change-database/transaction-mode/bb-transaction-mode-mysql.webp" />

### Available Isolation Levels

* **READ UNCOMMITTED**: Lowest isolation, allows dirty reads
* **READ COMMITTED**: Prevents dirty reads, allows non-repeatable reads
* **REPEATABLE READ**: Default MySQL isolation, prevents dirty and non-repeatable reads
* **SERIALIZABLE**: Highest isolation, prevents all phenomena but may impact performance

## GitOps

In the GitOps workflow there is no UI toggle, so transaction mode and isolation level are controlled via comment directives at the top of the migration file:

```sql theme={null}
-- txn-mode = off
-- txn-isolation = READ COMMITTED

ALTER TABLE users ADD COLUMN email VARCHAR(255);
```

Supported directives:

* `-- txn-mode = on|off` — wrap the script in a transaction, or run statements directly without transaction wrapping
* `-- txn-isolation = READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE` — MySQL only

Rules:

* Directives must appear at the top of the file, before any non-comment SQL
* Empty lines between directives are allowed; scanning stops at the first non-comment line
* The order of the two directives does not matter
* Whitespace around `=` is optional — `-- txn-mode = off` and `-- txn-mode=off` are both accepted; the spaced form shown above is the canonical style

## Best Practices

* Keep transactions enabled for DDL and DML operations that modify data
* Consider disabling transactions only for:
  * Large batch operations that manage their own transaction boundaries
  * Statements that cannot run within a transaction block — see [Statements that cannot run in a transaction block](#statements-that-cannot-run-in-a-transaction-block)
* Choose appropriate isolation levels based on your concurrency requirements and performance needs
