Skip to main content

sqlparser/ast/
procedural.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18use core::fmt::{self, Display};
19
20#[cfg(not(feature = "std"))]
21use alloc::{boxed::Box, vec::Vec};
22#[cfg(feature = "std")]
23use std::boxed::Box;
24
25#[cfg(feature = "serde")]
26use serde::{Deserialize, Serialize};
27
28#[cfg(feature = "visitor")]
29use sqlparser_derive::{Visit, VisitMut};
30
31use super::{DataType, Expr, Ident, ObjectName, Statement, ValueWithSpan};
32
33#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
34#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
35#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
36/// A bounded PL/pgSQL block.
37pub struct PlPgSqlBlock {
38    /// Local variable declarations.
39    pub declarations: Vec<PlPgSqlDeclaration>,
40    /// Statements in the block body.
41    pub statements: Vec<PlPgSqlStatement>,
42}
43
44impl Display for PlPgSqlBlock {
45    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
46        if !self.declarations.is_empty() {
47            write!(f, "DECLARE ")?;
48            for declaration in &self.declarations {
49                write!(f, "{declaration}; ")?;
50            }
51        }
52        write!(f, "BEGIN")?;
53        for statement in &self.statements {
54            write!(f, " {statement};")?;
55        }
56        write!(f, " END")
57    }
58}
59
60#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
61#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
62#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
63/// A PL/pgSQL local variable declaration.
64pub struct PlPgSqlDeclaration {
65    /// Variable name.
66    pub name: Ident,
67    /// Declared SQL type.
68    pub data_type: DataType,
69    /// Optional initializer.
70    pub initializer: Option<Expr>,
71}
72
73impl Display for PlPgSqlDeclaration {
74    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
75        write!(f, "{} {}", self.name, self.data_type)?;
76        if let Some(initializer) = &self.initializer {
77            write!(f, " := {initializer}")?;
78        }
79        Ok(())
80    }
81}
82
83#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
84#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
85#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
86/// A statement in a bounded PL/pgSQL block.
87pub enum PlPgSqlStatement {
88    /// Assign an expression to a name or field.
89    Assignment {
90        /// Assignment target.
91        target: ObjectName,
92        /// Assigned expression.
93        value: Expr,
94    },
95    /// Conditional branches.
96    If {
97        /// Ordered `IF` and `ELSIF` branches.
98        branches: Vec<PlPgSqlIfBranch>,
99        /// Optional `ELSE` statements.
100        else_statements: Option<Vec<PlPgSqlStatement>>,
101    },
102    /// Return an expression.
103    Return(Expr),
104    /// Execute an embedded SQL statement.
105    Sql(Box<Statement>),
106    /// Assign the affected-row count of the preceding SQL statement.
107    GetRowCount {
108        /// Destination variable.
109        target: Ident,
110    },
111    /// Raise an exception with formatted arguments.
112    RaiseException {
113        /// Literal format string.
114        format: ValueWithSpan,
115        /// Format substitution arguments.
116        arguments: Vec<Expr>,
117        /// Optional error hint.
118        hint: Option<Expr>,
119    },
120}
121
122impl Display for PlPgSqlStatement {
123    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
124        match self {
125            Self::Assignment { target, value } => write!(f, "{target} := {value}"),
126            Self::If {
127                branches,
128                else_statements,
129            } => {
130                for (index, branch) in branches.iter().enumerate() {
131                    if index == 0 {
132                        write!(f, "IF {} THEN", branch.condition)?;
133                    } else {
134                        write!(f, " ELSIF {} THEN", branch.condition)?;
135                    }
136                    for statement in &branch.statements {
137                        write!(f, " {statement};")?;
138                    }
139                }
140                if let Some(statements) = else_statements {
141                    write!(f, " ELSE")?;
142                    for statement in statements {
143                        write!(f, " {statement};")?;
144                    }
145                }
146                write!(f, " END IF")
147            }
148            Self::Return(expression) => write!(f, "RETURN {expression}"),
149            Self::Sql(statement) => statement.fmt(f),
150            Self::GetRowCount { target } => {
151                write!(f, "GET DIAGNOSTICS {target} = ROW_COUNT")
152            }
153            Self::RaiseException {
154                format,
155                arguments,
156                hint,
157            } => {
158                write!(f, "RAISE EXCEPTION {format}")?;
159                for argument in arguments {
160                    write!(f, ", {argument}")?;
161                }
162                if let Some(hint) = hint {
163                    write!(f, " USING HINT = {hint}")?;
164                }
165                Ok(())
166            }
167        }
168    }
169}
170
171#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
172#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
173#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
174/// One conditional PL/pgSQL branch.
175pub struct PlPgSqlIfBranch {
176    /// Branch condition.
177    pub condition: Expr,
178    /// Statements run when the condition is true.
179    pub statements: Vec<PlPgSqlStatement>,
180}
181
182#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
183#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
184#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
185/// A PostgreSQL anonymous code block.
186pub struct DoStatement {
187    /// Raw string body.
188    pub body: ValueWithSpan,
189    /// Optional procedural language.
190    pub language: Option<Ident>,
191    /// Whether `LANGUAGE` appeared before the body.
192    pub language_before_body: bool,
193}
194
195impl Display for DoStatement {
196    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
197        write!(f, "DO")?;
198        if self.language_before_body {
199            if let Some(language) = &self.language {
200                write!(f, " LANGUAGE {language}")?;
201            }
202        }
203        write!(f, " {}", self.body)?;
204        if !self.language_before_body {
205            if let Some(language) = &self.language {
206                write!(f, " LANGUAGE {language}")?;
207            }
208        }
209        Ok(())
210    }
211}