Skip to main content

uqa_sql/semantics/effects/
transaction_blocks.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7//! Transaction commands that require an explicit SQL block.
8
9use crate::SQLError;
10
11pub fn transaction_requires_explicit_block(transaction: &crate::ast::TransactionStmt) -> bool {
12    matches!(
13        transaction,
14        crate::ast::TransactionStmt::Savepoint(_)
15            | crate::ast::TransactionStmt::ReleaseSavepoint(_)
16            | crate::ast::TransactionStmt::RollbackToSavepoint(_)
17            | crate::ast::TransactionStmt::CommitAndChain
18            | crate::ast::TransactionStmt::RollbackAndChain
19    )
20}
21
22pub fn no_active_transaction_error(transaction: &crate::ast::TransactionStmt) -> SQLError {
23    let command = match transaction {
24        crate::ast::TransactionStmt::Savepoint(_) => "SAVEPOINT",
25        crate::ast::TransactionStmt::ReleaseSavepoint(_) => "RELEASE SAVEPOINT",
26        crate::ast::TransactionStmt::RollbackToSavepoint(_) => "ROLLBACK TO SAVEPOINT",
27        crate::ast::TransactionStmt::CommitAndChain => "COMMIT AND CHAIN",
28        crate::ast::TransactionStmt::RollbackAndChain => "ROLLBACK AND CHAIN",
29        _ => unreachable!("only explicit-block transaction commands use this error"),
30    };
31    SQLError::Routine {
32        sqlstate: "25P01".into(),
33        message: format!("{command} can only be used in transaction blocks"),
34    }
35}
36
37/// The warning of `command`, a command whose effect lasts only until the end of its transaction, outside a transaction block, as `PostgreSQL`'s `WarnNoTransactionBlock` reports it: such a command lasts only for its own statement. A command inside a function, or inside a block, has a transaction to last for and is not warned about.
38pub fn no_transaction_block_warning(command: &str) -> crate::SQLNotice {
39    crate::SQLNotice::warning(format!("{command} can only be used in transaction blocks"))
40        .with_sqlstate("25P01")
41}
42
43/// The warning of a `BEGIN` inside a transaction block, or inside one of its subtransactions, which `PostgreSQL`'s `BeginTransactionBlock` reports and otherwise ignores.
44pub fn transaction_in_progress_warning() -> crate::SQLNotice {
45    crate::SQLNotice::warning("there is already a transaction in progress").with_sqlstate("25001")
46}
47
48/// The warning of a `COMMIT` or `ROLLBACK` outside a transaction block, which `PostgreSQL`'s `EndTransactionBlock` and `UserAbortTransactionBlock` report and otherwise ignore.
49pub fn no_transaction_in_progress_warning() -> crate::SQLNotice {
50    crate::SQLNotice::warning("there is no transaction in progress").with_sqlstate("25P01")
51}
52
53#[cfg(test)]
54mod tests {
55    use super::{no_active_transaction_error, transaction_requires_explicit_block};
56    use crate::{ast::TransactionStmt, SQLError};
57
58    #[test]
59    fn explicit_block_commands_keep_their_individual_diagnostics() {
60        for (command, spelling) in [
61            (TransactionStmt::Savepoint("s".into()), "SAVEPOINT"),
62            (
63                TransactionStmt::ReleaseSavepoint("s".into()),
64                "RELEASE SAVEPOINT",
65            ),
66            (
67                TransactionStmt::RollbackToSavepoint("s".into()),
68                "ROLLBACK TO SAVEPOINT",
69            ),
70            (TransactionStmt::CommitAndChain, "COMMIT AND CHAIN"),
71            (TransactionStmt::RollbackAndChain, "ROLLBACK AND CHAIN"),
72        ] {
73            assert!(transaction_requires_explicit_block(&command));
74            assert!(
75                matches!(no_active_transaction_error(&command), SQLError::Routine { sqlstate, message } if sqlstate == "25P01" && message == format!("{spelling} can only be used in transaction blocks"))
76            );
77        }
78        for command in [
79            TransactionStmt::Begin,
80            TransactionStmt::Commit,
81            TransactionStmt::Rollback,
82        ] {
83            assert!(!transaction_requires_explicit_block(&command));
84        }
85    }
86}