Skip to main content

sea_orm/database/
statement.rs

1use crate::DbBackend;
2#[cfg(feature = "rbac")]
3pub use sea_query::audit::{AuditTrait, Error as AuditError, QueryAccessAudit};
4use sea_query::{MysqlQueryBuilder, PostgresQueryBuilder, SqliteQueryBuilder, inject_parameters};
5pub use sea_query::{Value, Values};
6use std::fmt;
7
8/// A SQL string together with its bound parameters, ready to send to a
9/// connection. Build one yourself with
10/// [`from_sql_and_values`](Self::from_sql_and_values) (or the
11/// [`raw_sql!`](crate::raw_sql) macro), or get one out of any query builder
12/// via [`QueryTrait::build`](crate::QueryTrait::build).
13#[derive(Debug, Clone, PartialEq)]
14pub struct Statement {
15    /// The SQL text, with backend-specific placeholders for the values.
16    pub sql: String,
17    /// Bound parameter values, in the order they appear in `sql`. `None`
18    /// means the statement has no parameters.
19    pub values: Option<Values>,
20    /// Backend this statement was built for; both the SQL dialect and the
21    /// placeholder style must match.
22    pub db_backend: DbBackend,
23}
24
25/// Anything that can be rendered to a backend-specific [`Statement`].
26///
27/// Implemented by the `sea_query` statement types (`SelectStatement`,
28/// `InsertStatement`, etc.) so they can be passed to
29/// [`ConnectionTrait::execute`](crate::ConnectionTrait::execute) /
30/// [`query_all`](crate::ConnectionTrait::query_all) directly.
31pub trait StatementBuilder {
32    /// Render `self` into a [`Statement`] for `db_backend`.
33    fn build(&self, db_backend: &DbBackend) -> Statement;
34
35    #[cfg(feature = "rbac")]
36    /// Inspect the statement and produce the access request that
37    /// [`RbacEngine`](crate::rbac::RbacEngine) needs to authorise it.
38    fn audit(&self) -> Result<QueryAccessAudit, AuditError>;
39}
40
41impl Statement {
42    /// Create a [Statement] from a [crate::DatabaseBackend] and a raw SQL statement
43    pub fn from_string<T>(db_backend: DbBackend, stmt: T) -> Statement
44    where
45        T: Into<String>,
46    {
47        Statement {
48            sql: stmt.into(),
49            values: None,
50            db_backend,
51        }
52    }
53
54    /// Create a SQL statement from a [crate::DatabaseBackend], a
55    /// raw SQL statement and param values
56    pub fn from_sql_and_values<I, T>(db_backend: DbBackend, sql: T, values: I) -> Self
57    where
58        I: IntoIterator<Item = Value>,
59        T: Into<String>,
60    {
61        Self::from_string_values_tuple(db_backend, (sql, Values(values.into_iter().collect())))
62    }
63
64    pub(crate) fn from_string_values_tuple<T>(db_backend: DbBackend, stmt: (T, Values)) -> Statement
65    where
66        T: Into<String>,
67    {
68        Statement {
69            sql: stmt.0.into(),
70            values: Some(stmt.1),
71            db_backend,
72        }
73    }
74}
75
76impl fmt::Display for Statement {
77    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
78        match &self.values {
79            Some(values) => {
80                let string = match self.db_backend {
81                    DbBackend::MySql => inject_parameters(&self.sql, &values.0, &MysqlQueryBuilder),
82                    DbBackend::Postgres => {
83                        inject_parameters(&self.sql, &values.0, &PostgresQueryBuilder)
84                    }
85                    DbBackend::Sqlite => {
86                        inject_parameters(&self.sql, &values.0, &SqliteQueryBuilder)
87                    }
88                };
89                write!(f, "{}", &string)
90            }
91            None => {
92                write!(f, "{}", &self.sql)
93            }
94        }
95    }
96}
97
98macro_rules! build_any_stmt {
99    ($stmt: expr, $db_backend: expr) => {
100        match $db_backend {
101            DbBackend::MySql => $stmt.build(MysqlQueryBuilder),
102            DbBackend::Postgres => $stmt.build(PostgresQueryBuilder),
103            DbBackend::Sqlite => $stmt.build(SqliteQueryBuilder),
104        }
105    };
106}
107
108macro_rules! build_postgres_stmt {
109    ($stmt: expr, $db_backend: expr) => {
110        match $db_backend {
111            DbBackend::Postgres => $stmt.to_string(PostgresQueryBuilder),
112            DbBackend::MySql | DbBackend::Sqlite => unimplemented!(),
113        }
114    };
115}
116
117macro_rules! build_query_stmt {
118    ($stmt: ty) => {
119        impl StatementBuilder for $stmt {
120            fn build(&self, db_backend: &DbBackend) -> Statement {
121                let stmt = build_any_stmt!(self, db_backend);
122                Statement::from_string_values_tuple(*db_backend, stmt)
123            }
124
125            #[cfg(feature = "rbac")]
126            fn audit(&self) -> Result<QueryAccessAudit, AuditError> {
127                AuditTrait::audit(self)
128            }
129        }
130    };
131}
132
133build_query_stmt!(sea_query::InsertStatement);
134build_query_stmt!(sea_query::SelectStatement);
135build_query_stmt!(sea_query::UpdateStatement);
136build_query_stmt!(sea_query::DeleteStatement);
137build_query_stmt!(sea_query::WithQuery);
138
139macro_rules! build_schema_stmt {
140    ($stmt: ty) => {
141        impl StatementBuilder for $stmt {
142            fn build(&self, db_backend: &DbBackend) -> Statement {
143                let stmt = build_any_stmt!(self, db_backend);
144                Statement::from_string(*db_backend, stmt)
145            }
146
147            #[cfg(feature = "rbac")]
148            fn audit(&self) -> Result<QueryAccessAudit, AuditError> {
149                todo!("Audit not supported for {} yet", stringify!($stmt))
150            }
151        }
152    };
153}
154
155build_schema_stmt!(sea_query::TableCreateStatement);
156build_schema_stmt!(sea_query::TableDropStatement);
157build_schema_stmt!(sea_query::TableAlterStatement);
158build_schema_stmt!(sea_query::TableRenameStatement);
159build_schema_stmt!(sea_query::TableTruncateStatement);
160build_schema_stmt!(sea_query::IndexCreateStatement);
161build_schema_stmt!(sea_query::IndexDropStatement);
162build_schema_stmt!(sea_query::ForeignKeyCreateStatement);
163build_schema_stmt!(sea_query::ForeignKeyDropStatement);
164
165macro_rules! build_type_stmt {
166    ($stmt: ty) => {
167        impl StatementBuilder for $stmt {
168            fn build(&self, db_backend: &DbBackend) -> Statement {
169                let stmt = build_postgres_stmt!(self, db_backend);
170                Statement::from_string(*db_backend, stmt)
171            }
172
173            #[cfg(feature = "rbac")]
174            fn audit(&self) -> Result<QueryAccessAudit, AuditError> {
175                Err(AuditError::UnsupportedQuery)
176            }
177        }
178    };
179}
180
181build_type_stmt!(sea_query::extension::postgres::TypeAlterStatement);
182build_type_stmt!(sea_query::extension::postgres::TypeCreateStatement);
183build_type_stmt!(sea_query::extension::postgres::TypeDropStatement);