` | If using [advanced client-side transaction retries](advanced-client-side-transaction-retries.html), retry the transaction. You should execute this statement when a transaction returns a `40001` / `retry transaction` error.
## Example
+### Using rollbacks with nested savepoints
+
+For examples showing how to use `ROLLBACK` with nested savepoints, see [the `SAVEPOINT` documentation](savepoint.html).
+
### Rollback a transaction
Typically, an application conditionally executes rollbacks, but we can see their behavior by using `ROLLBACK` instead of `COMMIT` directly through SQL:
@@ -77,15 +87,16 @@ To use [advanced client-side transaction retries](advanced-client-side-transacti
{% include copy-clipboard.html %}
~~~ sql
-> ROLLBACK TO SAVEPOINT cockroach_restart;
+> ROLLBACK TO SAVEPOINT my_retry_savepoint;
~~~
For examples of retrying transactions in an application, check out the transaction code samples in our [Build an App with CockroachDB](build-an-app-with-cockroachdb.html) tutorials.
## See also
+- [`SAVEPOINT`](savepoint.html)
- [Transactions](transactions.html)
- [`BEGIN`](begin-transaction.html)
- [`COMMIT`](commit-transaction.html)
-- [`SAVEPOINT`](savepoint.html)
- [`RELEASE SAVEPOINT`](release-savepoint.html)
+- [`SHOW SAVEPOINT STATUS`](show-savepoint-status.html)
diff --git a/v20.1/savepoint.md b/v20.1/savepoint.md
index b2f5fe4be51..7021f8b85c0 100644
--- a/v20.1/savepoint.md
+++ b/v20.1/savepoint.md
@@ -1,12 +1,12 @@
---
title: SAVEPOINT
-summary: Identify your intent to retry aborted transactions with the SAVEPOINT statement in CockroachDB.
+summary: Start a sub-transaction.
toc: true
---
-The `SAVEPOINT` statement defines the intent to retry [transactions](transactions.html) using the CockroachDB-provided function for client-side transaction retries. For more information, see [Transaction Retries](transactions.html#transaction-retries).
+A savepoint is a marker that defines the beginning of a [sub-transaction](transactions.html#sub-transactions). This marker can be later used to commit or roll back just the effects of the sub-transaction without affecting the progress of the enclosing (sub-)transaction. Sub-transactions can be nested.
-{% include {{ page.version.version }}/misc/savepoint-limitations.md %}
+{% include {{page.version.version}}/sql/savepoint-ddl-rollbacks.md %}
## Synopsis
@@ -22,17 +22,205 @@ No [privileges](authorization.html#assign-privileges) are required to create a s
Parameter | Description
--------- | -----------
-name | The name of the savepoint. Defaults to `cockroach_restart`, but may be customized. For more information, see [Customizing the savepoint name](#customizing-the-savepoint-name).
+name | The name of the savepoint.
-## Customizing the savepoint name
+## Examples
-{% include {{ page.version.version }}/misc/customizing-the-savepoint-name.md %}
+The examples below use the following table:
-## Example
+{% include copy-clipboard.html %}
+~~~ sql
+CREATE TABLE kv (k INT PRIMARY KEY, v INT);
+~~~
+
+### Basic usage
+
+To establish a savepoint inside a transaction:
+
+{% include copy-clipboard.html %}
+~~~ sql
+SAVEPOINT foo;
+~~~
+
+{{site.data.alerts.callout_info}}
+Due to the [rules for identifiers in our SQL grammar](keywords-and-identifiers.html#identifiers), `SAVEPOINT foo` and `SAVEPOINT Foo` define the same savepoint, whereas `SAVEPOINT "Foo"` defines another.
+{{site.data.alerts.end}}
+
+To roll back a transaction partially to a previously established savepoint:
+
+{% include copy-clipboard.html %}
+~~~ sql
+ROLLBACK TO SAVEPOINT foo;
+~~~
+
+To forget a savepoint, and keep the effects of statements executed after the savepoint was established, use [`RELEASE SAVEPOINT`](release-savepoint.html):
+
+{% include copy-clipboard.html %}
+~~~ sql
+RELEASE SAVEPOINT foo;
+~~~
+
+For example, the transaction below will insert the values `(1,1)` and `(3,3)` into the table, but not `(2,2)`:
+
+{% include copy-clipboard.html %}
+~~~ sql
+BEGIN;
+INSERT INTO kv VALUES (1,1);
+SAVEPOINT my_savepoint;
+INSERT INTO kv VALUES (2,2);
+ROLLBACK TO SAVEPOINT my_savepoint;
+INSERT INTO kv VALUES (3,3);
+COMMIT;
+~~~
+
+### Nested savepoints
-After you `BEGIN` the transaction, you must create the savepoint to identify that if the transaction contends with another transaction for resources and "loses", you intend to use [client-side transaction retries](transactions.html#transaction-retries).
+Savepoints can be nested. [`RELEASE SAVEPOINT`](release-savepoint.html) and [`ROLLBACK TO SAVEPOINT`](rollback-transaction.html) can both refer to a savepoint "higher" in the nesting hierarchy. When this occurs, all of the savepoints "under" the nesting are automatically released / rolled back too. Specifically:
-Applications using `SAVEPOINT` must also include functions to execute retries with [`ROLLBACK TO SAVEPOINT `](rollback-transaction.html#retry-a-transaction).
+- When a previous savepoint is rolled back, the statements entered after that savepoint are also rolled back.
+
+- When a previous savepoint is released, it commits; the statements entered after that savepoint are also committed.
+
+### Multi-level rollback with `ROLLBACK`
+
+Savepoints can be arbitrarily nested, and rolled back to the outermost level so that every subsequent statement is rolled back.
+
+For example, this transaction does not insert anything into the table. Both `INSERT`s are rolled back:
+
+{% include copy-clipboard.html %}
+~~~ sql
+BEGIN;
+SAVEPOINT foo;
+INSERT INTO kv VALUES (5,5);
+SAVEPOINT bar;
+INSERT INTO kv VALUES (6,6);
+ROLLBACK TO SAVEPOINT foo;
+COMMIT;
+~~~
+
+### Multi-level commit with `RELEASE SAVEPOINT`
+
+Changes committed by releasing a savepoint commit all of the statements entered after that savepoint.
+
+For example, the following transaction inserts both `(2,2)` and `(4,4)` into the table when it releases the outermost savepoint:
+
+{% include copy-clipboard.html %}
+~~~ sql
+BEGIN;
+SAVEPOINT foo;
+INSERT INTO kv VALUES (2,2);
+SAVEPOINT bar;
+INSERT INTO kv VALUES (4,4);
+RELEASE SAVEPOINT foo;
+COMMIT;
+~~~
+
+### Multi-level rollback and commit in the same transaction
+
+Changes partially committed by a savepoint release can be rolled back by an outer savepoint.
+
+For example, the following transaction inserts only value `(5, 5)`. The values `(6,6)` and `(7,7)` are rolled back.
+
+{% include copy-clipboard.html %}
+~~~ sql
+BEGIN;
+INSERT INTO kv VALUES (5,5);
+SAVEPOINT foo;
+INSERT INTO kv VALUES (6,6);
+SAVEPOINT bar;
+INSERT INTO kv VALUES (7,7);
+RELEASE SAVEPOINT bar;
+ROLLBACK TO SAVEPOINT foo;
+COMMIT;
+~~~
+
+### Savepoint name visibility
+
+The name of a savepoint that was rolled back over is no longer visible afterward.
+
+For example, in the transaction below, the name "bar" is not visible after it was rolled back over:
+
+{% include copy-clipboard.html %}
+~~~ sql
+BEGIN;
+SAVEPOINT foo;
+SAVEPOINT bar;
+ROLLBACK TO SAVEPOINT foo;
+RELEASE SAVEPOINT bar;
+COMMIT;
+~~~
+
+~~~
+ERROR: savepoint bar does not exist
+SQLSTATE: 3B001
+~~~
+
+The [SQL client](cockroach-sql.html) prompt will now display an error state, which you can clear by entering [`ROLLBACK`](rollback-transaction.html):
+
+{% include copy-clipboard.html %}
+~~~ sql
+? ERROR> ROLLBACK;
+~~~
+
+~~~
+ROLLBACK
+~~~
+
+#### Savepoints and prepared statements
+
+Prepared statements (`PREPARE` / `EXECUTE`) are not transactional. Therefore, prepared statements are not invalidated upon savepoint rollback. As a result, the prepared statement was saved and executed inside the transaction, despite the rollback to the prior savepoint:
+
+{% include copy-clipboard.html %}
+~~~ sql
+BEGIN;
+SAVEPOINT foo;
+PREPARE bar AS SELECT 1;
+ROLLBACK TO SAVEPOINT foo;
+EXECUTE bar;
+COMMIT;
+~~~
+
+~~~
+ ?column?
+------------
+ 1
+(1 row)
+~~~
+
+
+
+### Savepoints for client-side transaction retries
+
+If you intend to implement [advanced client-side transaction retries](advanced-client-side-transaction-retries.html), after you `BEGIN` the transaction, you must create an outermost savepoint. This will identify that if the transaction contends with another transaction for resources and "loses", you intend to use [client-side transaction retries](transactions.html#transaction-retries).
+
+Applications using `SAVEPOINT` for client-side transaction retries must also include functions to execute retries with [`ROLLBACK TO SAVEPOINT `](rollback-transaction.html#retry-a-transaction).
+
+A savepoint used for client-side transaction retries must be the outermost savepoint in a transaction; it cannot be nested inside other savepoints.
{% include copy-clipboard.html %}
~~~ sql
@@ -41,7 +229,7 @@ Applications using `SAVEPOINT` must also include functions to execute retries wi
{% include copy-clipboard.html %}
~~~ sql
-> SAVEPOINT cockroach_restart;
+> SAVEPOINT my_retry_savepoint;
~~~
{% include copy-clipboard.html %}
@@ -56,7 +244,7 @@ Applications using `SAVEPOINT` must also include functions to execute retries wi
{% include copy-clipboard.html %}
~~~ sql
-> RELEASE SAVEPOINT cockroach_restart;
+> RELEASE SAVEPOINT my_retry_savepoint;
~~~
{% include copy-clipboard.html %}
@@ -64,12 +252,32 @@ Applications using `SAVEPOINT` must also include functions to execute retries wi
> COMMIT;
~~~
+### Showing savepoint status
+
+Use the [`SHOW SAVEPOINT STATUS`](show-savepoint-status.html) statement to see how many savepoints are active in the current transaction:
+
+{% include copy-clipboard.html %}
+~~~ sql
+SHOW SAVEPOINT STATUS;
+~~~
+
+~~~
+ savepoint_name | is_initial_savepoint
+-----------------+-----------------------
+ foo | false
+ bar | false
+(2 rows)
+~~~
+
+Note that the `is_initial_savepoint` column will be true if the savepoint is the outermost savepoint in the transaction.
+
## See also
-- [Transactions](transactions.html)
+- [`SHOW SAVEPOINT STATUS`](show-savepoint-status.html)
- [`RELEASE SAVEPOINT`](release-savepoint.html)
- [`ROLLBACK`](rollback-transaction.html)
- [`BEGIN`](begin-transaction.html)
- [`COMMIT`](commit-transaction.html)
+- [Transactions](transactions.html)
- [Retryable transaction example code in Java using JDBC](build-a-java-app-with-cockroachdb.html)
- [CockroachDB Architecture: Transaction Layer](architecture/transaction-layer.html)
diff --git a/v20.1/set-transaction.md b/v20.1/set-transaction.md
index 7cbab1e9941..fefbe24cfe3 100644
--- a/v20.1/set-transaction.md
+++ b/v20.1/set-transaction.md
@@ -44,7 +44,7 @@ CockroachDB now only supports `SERIALIZABLE` isolation, so transactions can no l
{% include copy-clipboard.html %}
~~~ sql
-> SAVEPOINT cockroach_restart;
+> SAVEPOINT my_savepoint;
~~~
{% include copy-clipboard.html %}
@@ -59,7 +59,7 @@ CockroachDB now only supports `SERIALIZABLE` isolation, so transactions can no l
{% include copy-clipboard.html %}
~~~ sql
-> RELEASE SAVEPOINT cockroach_restart;
+> RELEASE SAVEPOINT my_savepoint;
~~~
{% include copy-clipboard.html %}
diff --git a/v20.1/show-savepoint-status.md b/v20.1/show-savepoint-status.md
new file mode 100644
index 00000000000..aa97fbf8fdd
--- /dev/null
+++ b/v20.1/show-savepoint-status.md
@@ -0,0 +1,71 @@
+---
+title: SHOW SAVEPOINT STATUS
+summary: The SHOW SAVEPOINT STATUS statement lists the active savepoints in the current transaction.
+toc: true
+---
+
+The `SHOW SAVEPOINT STATUS` [statement](sql-statements.html) lists the active [savepoints](savepoint.html) in the current [transaction](transactions.html).
+
+## Required privileges
+
+No [privileges](authorization.html#assign-privileges) are required to create or show a savepoint. However, privileges are required for each statement within a transaction.
+
+## Synopsis
+
+
+ {% include {{ page.version.version }}/sql/diagrams/show_savepoint_status.html %}
+
+
+## Response
+
+The following fields are returned for each savepoint.
+
+Field | Description
+------|------------
+`savepoint_name` | The name of the savepoint.
+`is_initial_savepoint` | Whether the savepoint is the outermost savepoint in the transaction.
+
+## Example
+
+First, open a [transaction](transactions.html) using [`BEGIN`](begin-transaction.html), and create a [sub-transaction](transactions.html#sub-transactions) using a [savepoint](savepoint.html):
+
+{% include copy-clipboard.html %}
+~~~ sql
+BEGIN;
+SAVEPOINT foo;
+~~~
+
+Next, use the `SHOW SAVEPOINT STATUS` statement to list the active savepoints in the current sub-transaction.
+
+{% include copy-clipboard.html %}
+~~~ sql
+SHOW SAVEPOINT STATUS;
+~~~
+
+~~~
+ savepoint_name | is_initial_savepoint
+-----------------+-----------------------
+ foo | true
+(1 row)
+~~~
+
+Currently, there is only one savepoint.
+
+We can commit this sub-transaction by issuing the [`RELEASE SAVEPOINT`](release-savepoint.html) statement. Then, we clear the connection for the next transaction by issuing a [`COMMIT`](commit-transaction.html) statement.
+
+{% include copy-clipboard.html %}
+~~~ sql
+RELEASE SAVEPOINT foo;
+COMMIT;
+~~~
+
+If we did not want to commit this sub-transaction, but restart it instead, we would have issued a [`ROLLBACK TO SAVEPOINT`](rollback-transaction.html#using-rollbacks-with-nested-savepoints).
+
+## See also
+
+- [`SAVEPOINT`](savepoint.html)
+- [`RELEASE SAVEPOINT`](release-savepoint.html)
+- [`ROLLBACK`](rollback-transaction.html)
+- [`BEGIN`](begin-transaction.html)
+- [`COMMIT`](commit-transaction.html)
+- [Transactions](transactions.html)
diff --git a/v20.1/transactions.md b/v20.1/transactions.md
index feecd97c873..82400cd6db6 100644
--- a/v20.1/transactions.md
+++ b/v20.1/transactions.md
@@ -41,11 +41,11 @@ To use [advanced client-side transaction retries](advanced-client-side-transacti
~~~ sql
> BEGIN;
-> SAVEPOINT cockroach_restart;
+> SAVEPOINT my_retry_savepoint;
-> RELEASE SAVEPOINT cockroach_restart;
+> RELEASE SAVEPOINT my_retry_savepoint;
> COMMIT;
~~~
@@ -166,6 +166,14 @@ Transactions in CockroachDB lock data resources that are written during their ex
For more details about transaction contention and best practices for avoiding contention, see [Understanding and Avoiding Transaction Contention](performance-best-practices-overview.html#understanding-and-avoiding-transaction-contention).
+## Sub-transactions
+
+New in v20.1: CockroachDB supports the nesting of transactions using [savepoints](savepoint.html). These nested transactions are also known as sub-transactions.
+
+Just as [`COMMIT`](commit-transaction.html) and [`ROLLBACK`](rollback-transaction.html) are used to commit and discard entire transactions, respectively, [`RELEASE SAVEPOINT`](release-savepoint.html) and [`ROLLBACK TO SAVEPOINT`](rollback-transaction.html#using-rollbacks-with-nested-savepoints) are used to commit and discard sub-transactions.
+
+For more information, including examples showing how to use savepoints to create sub-transactions, see [the savepoints documentation](savepoint.html#examples).
+
## Transaction priorities
Every transaction in CockroachDB is assigned an initial **priority**. By default, that priority is `NORMAL`, but for transactions that should be given preference in [high-contention scenarios](performance-best-practices-overview.html#understanding-and-avoiding-transaction-contention), the client can set the priority within the [`BEGIN`](begin-transaction.html) statement: