Skip to main content

mz_sql/plan/statement/
tcl.rs

1// Copyright Materialize, Inc. and contributors. All rights reserved.
2//
3// Use of this software is governed by the Business Source License
4// included in the LICENSE file.
5//
6// As of the Change Date specified in that file, in accordance with
7// the Business Source License, use of this software will be governed
8// by the Apache License, Version 2.0.
9
10//! Transaction control language (TCL).
11//!
12//! This module houses the handlers for statements that manipulate the session's transaction,
13//! like `BEGIN` and `COMMIT`.
14
15use mz_sql_parser::ast::TransactionIsolationLevel;
16
17use crate::ast::{
18    CommitStatement, RollbackStatement, SetTransactionStatement, StartTransactionStatement,
19    TransactionAccessMode, TransactionMode,
20};
21use crate::plan::statement::{StatementContext, StatementDesc};
22use crate::plan::{
23    AbortTransactionPlan, CommitTransactionPlan, Plan, PlanError, SetTransactionPlan,
24    StartTransactionPlan, TransactionType,
25};
26
27pub fn describe_start_transaction(
28    _: &StatementContext,
29    _: StartTransactionStatement,
30) -> Result<StatementDesc, PlanError> {
31    Ok(StatementDesc::new(None))
32}
33
34pub fn plan_start_transaction(
35    _: &StatementContext,
36    StartTransactionStatement { modes }: StartTransactionStatement,
37) -> Result<Plan, PlanError> {
38    let (access, isolation_level) = verify_transaction_modes(modes)?;
39    Ok(Plan::StartTransaction(StartTransactionPlan {
40        access,
41        isolation_level,
42    }))
43}
44
45pub fn describe_set_transaction(
46    _: &StatementContext,
47    _: SetTransactionStatement,
48) -> Result<StatementDesc, PlanError> {
49    Ok(StatementDesc::new(None))
50}
51
52pub fn plan_set_transaction(
53    _: &StatementContext,
54    SetTransactionStatement { local, modes }: SetTransactionStatement,
55) -> Result<Plan, PlanError> {
56    // The sequencer applies each mode's side effect in order and rejects access
57    // modes outright. Reject them here, before any plan is produced, so a
58    // statement that cannot fully succeed leaves no partially-applied state
59    // behind (e.g. a silently changed isolation level). This mirrors
60    // `plan_start_transaction`, which validates all modes at plan time.
61    if modes
62        .iter()
63        .any(|m| matches!(m, TransactionMode::AccessMode(_)))
64    {
65        bail_unsupported!("SET TRANSACTION <access-mode>");
66    }
67    Ok(Plan::SetTransaction(SetTransactionPlan { local, modes }))
68}
69
70fn verify_transaction_modes(
71    modes: Vec<TransactionMode>,
72) -> Result<
73    (
74        Option<TransactionAccessMode>,
75        Option<TransactionIsolationLevel>,
76    ),
77    PlanError,
78> {
79    let mut access = None;
80    let mut isolation = None;
81    for mode in modes {
82        match mode {
83            TransactionMode::IsolationLevel(level) => isolation = Some(level),
84            TransactionMode::AccessMode(mode) => {
85                access = Some(mode);
86            }
87        }
88    }
89    Ok((access, isolation))
90}
91
92pub fn describe_rollback(
93    _: &StatementContext,
94    _: RollbackStatement,
95) -> Result<StatementDesc, PlanError> {
96    Ok(StatementDesc::new(None))
97}
98
99pub fn plan_rollback(
100    _: &StatementContext,
101    RollbackStatement { chain }: RollbackStatement,
102) -> Result<Plan, PlanError> {
103    verify_chain(chain)?;
104    Ok(Plan::AbortTransaction(AbortTransactionPlan {
105        transaction_type: TransactionType::Explicit,
106    }))
107}
108
109pub fn describe_commit(
110    _: &StatementContext,
111    _: CommitStatement,
112) -> Result<StatementDesc, PlanError> {
113    Ok(StatementDesc::new(None))
114}
115
116pub fn plan_commit(
117    _: &StatementContext,
118    CommitStatement { chain }: CommitStatement,
119) -> Result<Plan, PlanError> {
120    verify_chain(chain)?;
121    Ok(Plan::CommitTransaction(CommitTransactionPlan {
122        transaction_type: TransactionType::Explicit,
123    }))
124}
125
126fn verify_chain(chain: bool) -> Result<(), PlanError> {
127    if chain {
128        bail_unsupported!("CHAIN");
129    }
130    Ok(())
131}