diff --git a/_includes/sidebar-data-v20.1.json b/_includes/sidebar-data-v20.1.json index 74d6a045ca8..1a7ede1db27 100644 --- a/_includes/sidebar-data-v20.1.json +++ b/_includes/sidebar-data-v20.1.json @@ -1350,6 +1350,12 @@ "/${VERSION}/show-statistics.html" ] }, + { + "title": "SHOW SAVEPOINT STATUS", + "urls": [ + "/${VERSION}/show-savepoint-status.html" + ] + }, { "title": "SHOW TABLES", "urls": [ diff --git a/_includes/v20.1/misc/customizing-the-savepoint-name.md b/_includes/v20.1/misc/customizing-the-savepoint-name.md index 3608c3650b7..65166a4e5b2 100644 --- a/_includes/v20.1/misc/customizing-the-savepoint-name.md +++ b/_includes/v20.1/misc/customizing-the-savepoint-name.md @@ -1,7 +1,5 @@ - Set the `force_savepoint_restart` [session variable](set-vars.html#supported-variables) to `true` to enable using a custom name for the restart savepoint (for example, because you are using an ORM that wants to use its own names for savepoints). +Set the `force_savepoint_restart` [session variable](set-vars.html#supported-variables) to `true` to enable using a custom name for the retry savepoint (for example, because you are using an ORM that wants to use its own names for savepoints). -Once this variable is set, the [`SAVEPOINT`](savepoint.html) statement will accept any name for the savepoint, not just `cockroach_restart`. This allows compatibility with existing code that uses a single savepoint per transaction as long as that savepoint occurs before any statements that access data stored in non-virtual tables. +Once this variable is set, the [`SAVEPOINT`](savepoint.html) statement will accept any name for the retry savepoint, not just `cockroach_restart`. In addition, it causes every savepoint name to be equivalent to `cockroach_restart`, therefore disallowing the use of [generalized, nested savepoints](savepoint.html#nested-savepoints). -{{site.data.alerts.callout_danger}} -The `force_savepoint_restart` variable changes the semantics of CockroachDB savepoints so that `RELEASE SAVEPOINT ` functions as a real commit. Note that the existence of this variable and its behavior does not change the fact that CockroachDB savepoints can only be used as a part of the transaction retry protocol. -{{site.data.alerts.end}} +This behavior allows compatibility with existing code that uses a single savepoint per transaction as long as that savepoint occurs before any statements that access data stored in non-virtual tables. diff --git a/_includes/v20.1/misc/savepoint-limitations.md b/_includes/v20.1/misc/savepoint-limitations.md deleted file mode 100644 index 1232d1d3831..00000000000 --- a/_includes/v20.1/misc/savepoint-limitations.md +++ /dev/null @@ -1,3 +0,0 @@ -{{site.data.alerts.callout_danger}} -CockroachDB's [`SAVEPOINT`](savepoint.html) implementation does not support nested transactions (i.e., subtransactions). It is only used to handle [transaction retries](transactions.html#transaction-retries). -{{site.data.alerts.end}} diff --git a/_includes/v20.1/sql/diagrams/rollback_transaction.html b/_includes/v20.1/sql/diagrams/rollback_transaction.html index c34d5d12047..e981a160929 100644 --- a/_includes/v20.1/sql/diagrams/rollback_transaction.html +++ b/_includes/v20.1/sql/diagrams/rollback_transaction.html @@ -1,22 +1,17 @@ -
- - - - - - ROLLBACK - - - TO - - - SAVEPOINT - - - - cockroach_restart - - - - -
\ No newline at end of file +
+ + + + +ROLLBACK + + +TO + + +SAVEPOINT + + +savepoint_name + +
diff --git a/_includes/v20.1/sql/diagrams/show_savepoint_status.html b/_includes/v20.1/sql/diagrams/show_savepoint_status.html new file mode 100644 index 00000000000..7fc1c8fa52d --- /dev/null +++ b/_includes/v20.1/sql/diagrams/show_savepoint_status.html @@ -0,0 +1,15 @@ +
+ + + + +SHOW + + +SAVEPOINT + + +STATUS + + +
diff --git a/_includes/v20.1/sql/retry-savepoints.md b/_includes/v20.1/sql/retry-savepoints.md new file mode 100644 index 00000000000..47fdaf4f804 --- /dev/null +++ b/_includes/v20.1/sql/retry-savepoints.md @@ -0,0 +1,5 @@ +A savepoint defined with the name `cockroach_restart` is a "retry savepoint" and is used to implement [client-side transaction retries](savepoint.html#savepoints-for-client-side-transaction-retries). A retry savepoint differs from a [standard savepoint](savepoint.html#nested-savepoints) as follows: + +- It must be the outermost savepoint in the transaction. +- After a successful [`RELEASE`](release-savepoint.html), a retry savepoint does not allow further use of the transaction. The next statement must be a [`COMMIT`](commit-transaction.html). +- It cannot be nested. Issuing `SAVEPOINT cockroach_restart` two times in a row only creates a single savepoint marker (this can be verified with [`SHOW SAVEPOINT STATUS`](show-savepoint-status.html)). Issuing `SAVEPOINT cockroach_restart` after `ROLLBACK TO SAVEPOINT cockroach_restart` reuses the marker instead of creating a new one. diff --git a/_includes/v20.1/sql/savepoint-ddl-rollbacks.md b/_includes/v20.1/sql/savepoint-ddl-rollbacks.md new file mode 100644 index 00000000000..57da82ae775 --- /dev/null +++ b/_includes/v20.1/sql/savepoint-ddl-rollbacks.md @@ -0,0 +1,3 @@ +{{site.data.alerts.callout_danger}} +Rollbacks to savepoints over [DDL](https://en.wikipedia.org/wiki/Data_definition_language) statements are only supported if you're rolling back to a savepoint created at the beginning of the transaction. +{{site.data.alerts.end}} diff --git a/_includes/v20.1/sql/savepoints-and-row-locks.md b/_includes/v20.1/sql/savepoints-and-row-locks.md new file mode 100644 index 00000000000..d2c5595c08b --- /dev/null +++ b/_includes/v20.1/sql/savepoints-and-row-locks.md @@ -0,0 +1,12 @@ +CockroachDB supports exclusive row locks. + +- In PostgreSQL, row locks are released/cancelled upon [`ROLLBACK TO SAVEPOINT`][rts]. +- In CockroachDB, row locks are preserved upon [`ROLLBACK TO SAVEPOINT`][rts]. + +This is an architectural difference in v20.1 that may or may not be lifted in a later CockroachDB version. + +The code of client applications that rely on row locks must be reviewed and possibly modified to account for this difference. In particular, if an application is relying on [`ROLLBACK TO SAVEPOINT`][rts] to release row locks and allow a concurrent transaction touching the same rows to proceed, this behavior will not work with CockroachDB. + + + +[rts]: rollback-transaction.html diff --git a/_includes/v20.1/sql/unsupported-postgres-features.md b/_includes/v20.1/sql/unsupported-postgres-features.md index b1e28ed2c95..d40abbe932a 100644 --- a/_includes/v20.1/sql/unsupported-postgres-features.md +++ b/_includes/v20.1/sql/unsupported-postgres-features.md @@ -6,6 +6,5 @@ - GEOSPATIAL functions and indexes - Drop primary key - XML Functions -- Savepoints - Column-level privileges - XA syntax diff --git a/v20.1/advanced-client-side-transaction-retries.md b/v20.1/advanced-client-side-transaction-retries.md index da51aff0e3d..f5421fb51cd 100644 --- a/v20.1/advanced-client-side-transaction-retries.md +++ b/v20.1/advanced-client-side-transaction-retries.md @@ -12,7 +12,7 @@ If you are an application developer who needs to implement an application-level ## Overview -To improve the performance of transactions that fail due to contention, CockroachDB includes a set of statements (listed below) that let you retry those transactions. Retrying transactions using these statements has the following benefits: +To improve the performance of transactions that fail due to [contention](performance-best-practices-overview.html#understanding-and-avoiding-transaction-contention), CockroachDB includes a set of statements (listed below) that let you retry those transactions. Retrying transactions using these statements has the following benefits: 1. When you use savepoints, you "hold your place in line" between attempts. Without savepoints, you're starting from scratch every time. 2. Transactions increase their priority each time they're retried, increasing the likelihood they will succeed. This has a lesser effect than #1. @@ -32,7 +32,7 @@ A retryable transaction goes through the process described below, which maps to 1. The transaction starts with the [`BEGIN`](begin-transaction.html) statement. -2. The [`SAVEPOINT`](savepoint.html) statement declares the intention to retry the transaction in the case of contention errors. Note that CockroachDB's savepoint implementation does not support all savepoint functionality, such as nested transactions. It must be executed after [`BEGIN`](begin-transaction.html) but before the first statement that manipulates a database. +2. The [`SAVEPOINT`](savepoint.html) statement shown here is a retry savepoint; that is, it declares the intention to retry the transaction in the case of contention errors. It must be executed after [`BEGIN`](begin-transaction.html) but before the first statement that manipulates a database. Although [nested savepoints](savepoint.html#nested-savepoints) are supported in versions of CockroachDB >= 20.1, a retry savepoint must be the outermost savepoint in a transaction. 3. The statements in the transaction are executed. @@ -40,7 +40,7 @@ A retryable transaction goes through the process described below, which maps to You must now issue the statements in the transaction again. - In cases where you do not want the application to retry the transaction, you can simply issue [`ROLLBACK`](rollback-transaction.html) at this point. Any other statements will be rejected by the server, as is generally the case after an error has been encountered and the transaction has not been closed. + In cases where you do not want the application to retry the transaction, you can issue [`ROLLBACK`](rollback-transaction.html) at this point. Any other statements will be rejected by the server, as is generally the case after an error has been encountered and the transaction has not been closed. 5. Once the transaction executes all statements without encountering contention errors, execute [`RELEASE SAVEPOINT`](release-savepoint.html) to commit the changes. If this succeeds, all changes made by the transaction become visible to subsequent transactions and are guaranteed to be durable if a crash occurs. diff --git a/v20.1/commit-transaction.md b/v20.1/commit-transaction.md index 14952b07b6c..79393f0cc95 100644 --- a/v20.1/commit-transaction.md +++ b/v20.1/commit-transaction.md @@ -81,3 +81,4 @@ If you are using transactions that CockroachDB will [automatically retry](transa - [`RELEASE SAVEPOINT`](release-savepoint.html) - [`ROLLBACK`](rollback-transaction.html) - [`SAVEPOINT`](savepoint.html) +- [`SHOW SAVEPOINT STATUS`](show-savepoint-status.html) diff --git a/v20.1/detailed-sql-support.md b/v20.1/detailed-sql-support.md index d0a20d92c8b..73a55801978 100644 --- a/v20.1/detailed-sql-support.md +++ b/v20.1/detailed-sql-support.md @@ -486,7 +486,7 @@ To understand the extent to which we support the standard SQL features, use the | T241 | START TRANSACTION statement | Yes | | T251 | SET TRANSACTION statement: LOCAL option | No | | T261 | Chained transactions | No | -| T271 | Savepoints | No | +| T271 | Savepoints | Yes | | T272 | Enhanced savepoint management | No | | T281 | SELECT privilege with column granularity | No | | T285 | Enhanced derived column names | No | diff --git a/v20.1/release-savepoint.md b/v20.1/release-savepoint.md index e4cc00b6359..5b2749b23c9 100644 --- a/v20.1/release-savepoint.md +++ b/v20.1/release-savepoint.md @@ -1,16 +1,10 @@ --- title: RELEASE SAVEPOINT -summary: Commit a transaction's changes once there are no retry errors with the RELEASE SAVEPOINT statement in CockroachDB. +summary: Commit a sub-transaction. toc: true --- -When using [advanced client-side transaction retries](advanced-client-side-transaction-retries.html), the `RELEASE SAVEPOINT` statement commits the transaction. - -If statements in the transaction [generated any non-retry errors](transactions.html#error-handling), `RELEASE SAVEPOINT` is equivalent to [`ROLLBACK`](rollback-transaction.html), which aborts the transaction and discards all updates made by its statements. - -Note that although issuing this statement commits the transaction, you must also issue a subsequent [`COMMIT`](commit-transaction.html) statement to prepare the connection for the next transaction. - -{% include {{ page.version.version }}/misc/savepoint-limitations.md %} +The `RELEASE SAVEPOINT` statement commits the [sub-transaction](transactions.html#sub-transactions) starting at the corresponding `SAVEPOINT` statement using the same savepoint name, including all its nested sub-transactions. ## Synopsis @@ -26,39 +20,67 @@ No [privileges](authorization.html#assign-privileges) are required to release a 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. [Retry savepoints](savepoint.html#savepoints-for-client-side-transaction-retries) default to using the name `cockroach_restart`, but this can be customized using a session variable. For more information, see [Customizing the retry savepoint name](savepoint.html#customizing-the-retry-savepoint-name). -## Customizing the savepoint name +## Handling errors -{% include {{ page.version.version }}/misc/customizing-the-savepoint-name.md %} +The `RELEASE SAVEPOINT` statement is invalid after the sub-transaction has encountered an error. After an error, the [`ROLLBACK TO SAVEPOINT`](rollback-transaction.html#rollback-a-sub-transaction) statement must be used instead. + +When a (sub-)transaction encounters a retry error, the client should repeat `ROLLBACK TO SAVEPOINT` and the statements in the transaction until the statements complete without error, then issue `RELEASE`. + +To completely remove the marker of a sub-transaction after it encounters an error and begin other work in the outer transaction, use [`ROLLBACK TO SAVEPOINT`](rollback-transaction.html#rollback-a-sub-transaction) immediately followed by `RELEASE`. ## Examples -### Commit a Transaction +### Commit a sub-transaction by releasing a savepoint + +{{site.data.alerts.callout_info}} +This example uses the [MovR data set](movr.html). +{{site.data.alerts.end}} -After declaring a [`SAVEPOINT`](savepoint.html), commit the transaction with `RELEASE SAVEPOINT` and then prepare the connection for the next transaction with [`COMMIT`](commit-transaction.html): +In the example below, we roll back the inner [sub-transaction](transactions.html#sub-transactions) (marked by the savepoint `lower`) and release (commit) the outer savepoint `higher`, which raises the promo code discount to 15% using CockroachDB's [JSONB functions](jsonb.html#jsonb-functions). {% include copy-clipboard.html %} ~~~ sql -> BEGIN; +BEGIN; +SAVEPOINT higher; +UPDATE promo_codes SET rules = jsonb_set(rules, '{value}', '"15%"') WHERE rules @> '{"type": "percent_discount"}'; +SAVEPOINT lower; +UPDATE promo_codes SET rules = jsonb_set(rules, '{value}', '"7.5%"') WHERE rules @> '{"type": "percent_discount"}'; +rollback to savepoint lower; +release savepoint higher; +commit; +~~~ -> SAVEPOINT cockroach_restart; +~~~ +COMMIT +~~~ -> UPDATE products SET inventory = 0 WHERE sku = '8675309'; +### Commit a transaction by releasing a retry savepoint -> INSERT INTO orders (customer, sku, status) VALUES (1001, '8675309', 'new'); +{% include {{page.version.version}}/sql/retry-savepoints.md %} -> RELEASE SAVEPOINT cockroach_restart; +After declaring a retry savepoint, commit the transaction with `RELEASE SAVEPOINT` and then prepare the connection for the next transaction with [`COMMIT`](commit-transaction.html): -> COMMIT; +{% include copy-clipboard.html %} +~~~ sql +BEGIN; +SAVEPOINT cockroach_restart; +UPDATE products SET inventory = 0 WHERE sku = '8675309'; +INSERT INTO orders (customer, sku, status) VALUES (1001, '8675309', 'new'); +RELEASE SAVEPOINT cockroach_restart; +COMMIT; ~~~ -{{site.data.alerts.callout_danger}}This example assumes you're using client-side intervention to handle transaction retries.{{site.data.alerts.end}} +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). + +Note that you can [customize the retry savepoint name](savepoint.html#customizing-the-retry-savepoint-name) to something other than `cockroach_restart` with a session variable if you need to. ## See also - [Transactions](transactions.html) - [`SAVEPOINT`](savepoint.html) +- [`SHOW SAVEPOINT STATUS`](show-savepoint-status.html) - [`ROLLBACK`](rollback-transaction.html) - [`BEGIN`](begin-transaction.html) - [`COMMIT`](commit-transaction.html) diff --git a/v20.1/rollback-transaction.md b/v20.1/rollback-transaction.md index 3dc6c0f30f3..2776e1d9d44 100644 --- a/v20.1/rollback-transaction.md +++ b/v20.1/rollback-transaction.md @@ -1,12 +1,21 @@ --- title: ROLLBACK -summary: Abort the current transaction, discarding all updates made by statements included in the transaction with the ROLLBACK statement in CockroachDB. +summary: Rolls back the current transaction and all of its sub-transactions, discarding all transactional updates made by statements inside the transaction. toc: true --- -The `ROLLBACK` [statement](sql-statements.html) aborts the current [transaction](transactions.html), discarding all updates made by statements included in the transaction. +The `ROLLBACK` [statement](sql-statements.html) aborts the current [transaction](transactions.html) and all of its [sub-transactions](transactions.html#sub-transactions), discarding all transactional updates made by statements included in the transaction. -When using [advanced client-side transaction retries](advanced-client-side-transaction-retries.html), use `ROLLBACK TO SAVEPOINT` to handle a transaction that needs to be retried (identified via the `40001` error code or `retry transaction` string in the error message), and then re-execute the statements you want the transaction to contain. +`ROLLBACK` comes in two flavors: + +- The `ROLLBACK` statement [rolls back the entire transaction](#rollback-a-transaction). + +- The `ROLLBACK TO SAVEPOINT` statement [rolls back and restarts the sub-transaction](#rollback-a-sub-transaction) started at the corresponding `SAVEPOINT` statement. It can be used for working with [standard savepoints](savepoint.html#nested-savepoints) and for implementing [client-side transaction retries](transactions.html#client-side-intervention). For examples of each usage, see: + + - [Rollback a sub-transaction](#rollback-a-sub-transaction) + - [Retry a transaction](#retry-a-transaction) + +{% include {{page.version.version}}/sql/savepoint-ddl-rollbacks.md %} ## Synopsis @@ -23,8 +32,13 @@ No [privileges](authorization.html#assign-privileges) are required to rollback a Parameter | Description -----------|------------- `TO SAVEPOINT cockroach_restart` | 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. + `TO SAVEPOINT ` | If using [nested savepoints](savepoint.html#nested-savepoints), rolls back and restarts the [sub-transaction](transactions.html#sub-transactions) started at the corresponding `SAVEPOINT` statement. + +## Savepoints and row locks + +{% include {{page.version.version}}/sql/savepoints-and-row-locks.md %} -## Example +## Examples ### Rollback a transaction @@ -71,9 +85,15 @@ Typically, an application conditionally executes rollbacks, but we can see their +----------+---------+ ~~~ +### Rollback a sub-transaction + +The `ROLLBACK TO SAVEPOINT` statement rolls back and restarts the [sub-transaction](transactions.html#sub-transactions) started at the corresponding `SAVEPOINT` statement. + +For examples showing how to use `ROLLBACK TO SAVEPOINT` to rollback a sub-transaction, see [the `SAVEPOINT` documentation on nested savepoints](savepoint.html#nested-savepoints). + ### Retry a transaction -To use [advanced client-side transaction retries](advanced-client-side-transaction-retries.html), an application must execute `ROLLBACK TO SAVEPOINT` after detecting a `40001` / `retry transaction` error: +When using [advanced client-side transaction retries](advanced-client-side-transaction-retries.html), use `ROLLBACK TO SAVEPOINT` to handle a transaction that needs to be retried (identified via the `40001` error code or `retry transaction` string in the error message), and then re-execute the statements you want the transaction to contain. {% include copy-clipboard.html %} ~~~ sql @@ -84,8 +104,9 @@ For examples of retrying transactions in an application, check out the transacti ## 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..968ebc7f65f 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,54 +22,271 @@ 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. [Nested savepoints](savepoint.html#nested-savepoints) can use any name. [Retry savepoints](savepoint.html#savepoints-for-client-side-transaction-retries) default to using the name `cockroach_restart`, but this can be customized using a session variable. For more information, see [Customizing the retry savepoint name](savepoint.html#customizing-the-retry-savepoint-name). -## Customizing the savepoint name +## Savepoints and row locks -{% include {{ page.version.version }}/misc/customizing-the-savepoint-name.md %} +{% include {{page.version.version}}/sql/savepoints-and-row-locks.md %} -## Example +## Examples -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). +The examples below use the following table: -Applications using `SAVEPOINT` must also include functions to execute retries with [`ROLLBACK TO SAVEPOINT `](rollback-transaction.html#retry-a-transaction). +{% 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 + +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: + +- 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 TO SAVEPOINT` + +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; +~~~ + +### Error recovery in sub-transactions with `ROLLBACK TO SAVEPOINT` + +If `ROLLBACK TO SAVEPOINT` is used after a database error, it can also cancel the error state of the transaction. Database errors move a transaction (or a sub-transaction) into an "Aborted" state. In this state, the transaction will not execute any further SQL statements. + +You can use `ROLLBACK TO SAVEPOINT` to recover from a logical error in a sub-transaction. Logical errors include: + +- Unique index error (duplicate row) +- Failed foreign key constraint check (row does not exist in referenced table) +- Mistakes in queries (reference a column that does not exist) + +In addition, you can check the status of a sub-transaction using the `SHOW TRANSACTION STATUS` statement as shown below. + +For example: {% include copy-clipboard.html %} ~~~ sql -> BEGIN; +BEGIN; +SAVEPOINT error1; +INSERT INTO kv VALUES (5,5); -- Duplicate key error +~~~ + +~~~ +ERROR: duplicate key value (k)=(5) violates unique constraint "primary" +SQLSTATE: 23505 ~~~ {% include copy-clipboard.html %} ~~~ sql -> SAVEPOINT cockroach_restart; +SHOW TRANSACTION STATUS; +~~~ + +~~~ + TRANSACTION STATUS +---------------------- + Aborted +(1 row) +~~~ + +{% include copy-clipboard.html %} +~~~ sql +ROLLBACK TO SAVEPOINT error1; +INSERT INTO kv VALUES (6,6); +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 -> UPDATE products SET inventory = 0 WHERE sku = '8675309'; +? 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 -> INSERT INTO orders (customer, sku, status) VALUES (1001, '8675309', 'new'); +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 + +{% include {{page.version.version}}/sql/retry-savepoints.md %} + +The example below shows basic usage of a retry savepoint: + {% include copy-clipboard.html %} ~~~ sql -> RELEASE SAVEPOINT cockroach_restart; +BEGIN; +SAVEPOINT cockroach_restart; +UPDATE products SET inventory = 0 WHERE sku = '8675309'; +INSERT INTO orders (customer, sku, status) VALUES (1001, '8675309', 'new'); +RELEASE SAVEPOINT cockroach_restart; +COMMIT; ~~~ +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). + +Note that you can [customize the retry savepoint name](#customizing-the-retry-savepoint-name) to something other than `cockroach_restart` with a session variable if you need to. + +#### Customizing the retry savepoint name + +{% include {{page.version.version}}/misc/customizing-the-savepoint-name.md %} + +### 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 -> COMMIT; +SHOW SAVEPOINT STATUS; ~~~ +~~~ + savepoint_name | is_initial_savepoint +-----------------+----------------------- + foo | true + bar | false + baz | false +(3 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/show-savepoint-status.md b/v20.1/show-savepoint-status.md new file mode 100644 index 00000000000..d3bcc60f1de --- /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#rollback-a-sub-transaction). + +## 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/sql-feature-support.md b/v20.1/sql-feature-support.md index ff1182e0532..e631021963c 100644 --- a/v20.1/sql-feature-support.md +++ b/v20.1/sql-feature-support.md @@ -65,7 +65,7 @@ table tr td:nth-child(2) { `BEGIN` | ✓ | Standard | [`BEGIN` documentation](begin-transaction.html) `COMMIT` | ✓ | Standard | [`COMMIT` documentation](commit-transaction.html) `ROLLBACK` | ✓ | Standard | [`ROLLBACK` documentation](rollback-transaction.html) - `SAVEPOINT` | ✓ | CockroachDB Extension | While `SAVEPOINT` is part of the SQL standard, we only support [our extension of it](transactions.html#transaction-retries) + `SAVEPOINT` | ✓ | CockroachDB Extension | New in v20.1: CockroachDB supports nested sub-transactions using [`SAVEPOINT`](savepoint.html) ### Indexes diff --git a/v20.1/sql-statements.md b/v20.1/sql-statements.md index 76401b177e4..fed40db9b73 100644 --- a/v20.1/sql-statements.md +++ b/v20.1/sql-statements.md @@ -90,9 +90,10 @@ Statement | Usage ----------|------------ [`BEGIN`](begin-transaction.html)| Initiate a [transaction](transactions.html). [`COMMIT`](commit-transaction.html) | Commit the current [transaction](transactions.html). -[`RELEASE SAVEPOINT`](release-savepoint.html) | When using the CockroachDB-provided function for client-side [transaction retries](transactions.html#transaction-retries), commit the transaction's changes once there are no retry errors. -[`ROLLBACK`](rollback-transaction.html) | Discard all updates made by the current [transaction](transactions.html) or, when using the CockroachDB-provided function for client-side [transaction retries](transactions.html#transaction-retries), rollback to the savepoint and retry the transaction. -[`SAVEPOINT`](savepoint.html) | When using the CockroachDB-provided function for client-side [transaction retries](transactions.html#transaction-retries), start a retryable transaction. +[`SAVEPOINT`](savepoint.html) | Start a [sub-transaction](transactions.html#sub-transactions). +[`RELEASE SAVEPOINT`](release-savepoint.html) | Commit a [sub-transaction](transactions.html#sub-transactions). +[`ROLLBACK TO SAVEPOINT`](rollback-transaction.html#rollback-a-sub-transaction) | Roll back and restart the [sub-transaction](transactions.html#sub-transactions) started at the corresponding `SAVEPOINT` statement. +[`ROLLBACK`](rollback-transaction.html) | Rolls back the current [transaction](transactions.html) and all of its [sub-transaction](transactions.html#sub-transactions), discarding all transactional updates made by statements inside the transaction. [`SET TRANSACTION`](set-transaction.html) | Set the priority for the session or for an individual [transaction](transactions.html). [`SHOW`](show-vars.html) | View the current [transaction settings](transactions.html). diff --git a/v20.1/transactions.md b/v20.1/transactions.md index c66fbdd3db7..ca48a51c1e7 100644 --- a/v20.1/transactions.md +++ b/v20.1/transactions.md @@ -14,21 +14,19 @@ For a detailed discussion of CockroachDB transaction semantics, see [How Cockroa Each of the following SQL statements control transactions in some way. -| Statement | Function | -|------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| [`BEGIN`](begin-transaction.html) | Initiate a transaction, as well as control its [priority](#transaction-priorities). | -| [`SET TRANSACTION`](set-transaction.html) | Control a transaction's [priority](#transaction-priorities). | -| [`COMMIT`](commit-transaction.html) | Commit a regular transaction, or clear the connection after committing a transaction using the [advanced retry protocol](advanced-client-side-transaction-retries.html). | -| [`ROLLBACK`](rollback-transaction.html) | Abort a transaction and roll the database back to its state before the transaction began. | -| [`SHOW`](show-vars.html) | Display the current transaction settings. | -| [`SAVEPOINT`](savepoint.html) | (**Advanced**) Used to implement [advanced client-side transaction retries](advanced-client-side-transaction-retries.html), which can improve performance and avoid starvation when transactions are retried. | -| [`RELEASE SAVEPOINT`](release-savepoint.html) | (**Advanced**) Commit a [retryable transaction](advanced-client-side-transaction-retries.html). | -| [`ROLLBACK TO SAVEPOINT`](rollback-transaction.html) | (**Advanced**) Handle [retry errors](#error-handling) by rolling back a transaction's changes and increasing its priority. | +| Statement | Function | +|------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| [`BEGIN`](begin-transaction.html) | Initiate a transaction, as well as control its [priority](#transaction-priorities). | +| [`SET TRANSACTION`](set-transaction.html) | Control a transaction's [priority](#transaction-priorities). | +| [`COMMIT`](commit-transaction.html) | Commit a regular transaction, or clear the connection after committing a transaction using the [advanced retry protocol](advanced-client-side-transaction-retries.html). | +| [`ROLLBACK`](rollback-transaction.html) | Abort a transaction and roll the database back to its state before the transaction began. | +| [`SHOW`](show-vars.html) | Display the current transaction settings. | +| [`SAVEPOINT`](savepoint.html) | Used for [sub-transactions](#sub-transactions); also used to implement [advanced client-side transaction retries](advanced-client-side-transaction-retries.html). | +| [`RELEASE SAVEPOINT`](release-savepoint.html) | Commit a [sub-transaction](#sub-transactions); also used for [retryable transactions](advanced-client-side-transaction-retries.html). | +| [`ROLLBACK TO SAVEPOINT`](rollback-transaction.html) | Roll back a [sub-transaction](#sub-transactions); also used to handle [retryable transaction errors](advanced-client-side-transaction-retries.html). | {{site.data.alerts.callout_info}} -The **Advanced** statements above are used to implement [advanced client-side transaction retries](advanced-client-side-transaction-retries.html), and are mostly of use to driver and ORM authors. - -Application developers who are using a framework or library that does not have advanced retry logic built in should implement an application-level retry loop with exponential backoff as shown in [Client-side intervention](#client-side-intervention). +Application developers who are using a framework or library that does not have [advanced retry logic](advanced-client-side-transaction-retries.html) built in should implement an application-level retry loop with exponential backoff as shown in [Client-side intervention](#client-side-intervention). {{site.data.alerts.end}} ## Syntax @@ -156,7 +154,7 @@ To handle these types of errors you have the following options: - **Python** developers can use [SQLAlchemy](https://www.sqlalchemy.org) with the [`cockroachdb-python` adapter](https://github.com/cockroachdb/cockroachdb-python). For more information, see [Build a Python App with CockroachDB](build-a-python-app-with-cockroachdb-sqlalchemy.html). - **Java** developers accessing the database with [JDBC](https://jdbc.postgresql.org) can re-use the example code implementing retry logic shown in [Build a Java app with CockroachDB](build-a-java-app-with-cockroachdb.html). 2. **Most users, such as application authors**: Abort the transaction using the [`ROLLBACK`](rollback-transaction.html) statement, and then reissue all of the statements in the transaction. For an example, see the [Client-side intervention example](#client-side-intervention-example). -3. **Advanced users, such as library authors**: Use the [`SAVEPOINT`](savepoint.html) statement to create retryable transactions. Retryable transactions can improve performance because their priority is increased each time they are retried, making them more likely to succeed the longer they're in your system. For instructions showing how to do this, see [Advanced Client-Side Transaction Retries](advanced-client-side-transaction-retries.html). +3. **Advanced users, such as library authors**: See [Advanced Client-Side Transaction Retries](advanced-client-side-transaction-retries.html). {% include {{page.version.version}}/misc/mitigate-contention-note.md %} @@ -170,6 +168,28 @@ 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] and [`ROLLBACK`][rollback_transaction] are used to commit and discard entire transactions, respectively, [`RELEASE SAVEPOINT`][release_savepoint] and [`ROLLBACK TO SAVEPOINT`][rollback_to_savepoint] are used to commit and discard sub-transactions. This relationship is shown in the table below: + +| Statement | Effect | +|--------------------------------------------------+----------------------------------------------------| +| [`COMMIT`][commit] | Commit an entire transaction. | +| [`ROLLBACK`][rollback_transaction] | Discard an entire transaction. | +| [`RELEASE SAVEPOINT`][release_savepoint] | Commit (really, forget) the named sub-transaction. | +| [`ROLLBACK TO SAVEPOINT`][rollback_to_savepoint] | Discard the changes in the named sub-transaction. | + +For more information, including examples showing how to use savepoints to create sub-transactions, see [the savepoints documentation](savepoint.html#examples). + + + +[commit]: commit-transaction.html +[rollback]: rollback-transaction.html +[release_savepoint]: release-savepoint.html +[rollback_to_savepoint]: rollback-transaction.html#rollback-a-sub-transaction + ## 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: