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/// Defines an SQL statement
9#[derive(Debug, Clone, PartialEq)]
10pub struct Statement {
11    /// The SQL query
12    pub sql: String,
13    /// The values for the SQL statement's parameters
14    pub values: Option<Values>,
15    /// The database backend this statement is constructed for.
16    /// The SQL dialect and values should be valid for the DbBackend.
17    pub db_backend: DbBackend,
18}
19
20/// Any type that can build a [Statement]
21pub trait StatementBuilder: Sync {
22    /// Method to call in order to build a [Statement]
23    fn build(&self, db_backend: &DbBackend) -> Statement;
24
25    #[cfg(feature = "rbac")]
26    /// Method to audit access request of query
27    fn audit(&self) -> Result<QueryAccessAudit, AuditError>;
28}
29
30impl Statement {
31    /// Create a [Statement] from a [crate::DatabaseBackend] and a raw SQL statement
32    pub fn from_string<T>(db_backend: DbBackend, stmt: T) -> Statement
33    where
34        T: Into<String>,
35    {
36        Statement {
37            sql: stmt.into(),
38            values: None,
39            db_backend,
40        }
41    }
42
43    /// Create a SQL statement from a [crate::DatabaseBackend], a
44    /// raw SQL statement and param values
45    pub fn from_sql_and_values<I, T>(db_backend: DbBackend, sql: T, values: I) -> Self
46    where
47        I: IntoIterator<Item = Value>,
48        T: Into<String>,
49    {
50        Self::from_string_values_tuple(db_backend, (sql, Values(values.into_iter().collect())))
51    }
52
53    pub(crate) fn from_string_values_tuple<T>(db_backend: DbBackend, stmt: (T, Values)) -> Statement
54    where
55        T: Into<String>,
56    {
57        Statement {
58            sql: stmt.0.into(),
59            values: Some(stmt.1),
60            db_backend,
61        }
62    }
63}
64
65impl fmt::Display for Statement {
66    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
67        match &self.values {
68            Some(values) => {
69                let string = inject_parameters(
70                    &self.sql,
71                    values.0.clone(),
72                    self.db_backend.get_query_builder().as_ref(),
73                );
74                write!(f, "{}", &string)
75            }
76            None => {
77                write!(f, "{}", &self.sql)
78            }
79        }
80    }
81}
82
83macro_rules! build_any_stmt {
84    ($stmt: expr, $db_backend: expr) => {
85        match $db_backend {
86            DbBackend::MySql => $stmt.build(MysqlQueryBuilder),
87            DbBackend::Postgres => $stmt.build(PostgresQueryBuilder),
88            DbBackend::Sqlite => $stmt.build(SqliteQueryBuilder),
89        }
90    };
91}
92
93macro_rules! build_postgres_stmt {
94    ($stmt: expr, $db_backend: expr) => {
95        match $db_backend {
96            DbBackend::Postgres => $stmt.to_string(PostgresQueryBuilder),
97            DbBackend::MySql | DbBackend::Sqlite => unimplemented!(),
98        }
99    };
100}
101
102macro_rules! build_query_stmt {
103    ($stmt: ty) => {
104        impl StatementBuilder for $stmt {
105            fn build(&self, db_backend: &DbBackend) -> Statement {
106                let stmt = build_any_stmt!(self, db_backend);
107                Statement::from_string_values_tuple(*db_backend, stmt)
108            }
109
110            #[cfg(feature = "rbac")]
111            fn audit(&self) -> Result<QueryAccessAudit, AuditError> {
112                AuditTrait::audit(self)
113            }
114        }
115    };
116}
117
118build_query_stmt!(sea_query::InsertStatement);
119build_query_stmt!(sea_query::SelectStatement);
120build_query_stmt!(sea_query::UpdateStatement);
121build_query_stmt!(sea_query::DeleteStatement);
122build_query_stmt!(sea_query::WithQuery);
123
124macro_rules! build_schema_stmt {
125    ($stmt: ty) => {
126        impl StatementBuilder for $stmt {
127            fn build(&self, db_backend: &DbBackend) -> Statement {
128                let stmt = build_any_stmt!(self, db_backend);
129                Statement::from_string(*db_backend, stmt)
130            }
131
132            #[cfg(feature = "rbac")]
133            fn audit(&self) -> Result<QueryAccessAudit, AuditError> {
134                todo!("Audit not supported for {} yet", stringify!($stmt))
135            }
136        }
137    };
138}
139
140build_schema_stmt!(sea_query::TableCreateStatement);
141build_schema_stmt!(sea_query::TableDropStatement);
142build_schema_stmt!(sea_query::TableAlterStatement);
143build_schema_stmt!(sea_query::TableRenameStatement);
144build_schema_stmt!(sea_query::TableTruncateStatement);
145build_schema_stmt!(sea_query::IndexCreateStatement);
146build_schema_stmt!(sea_query::IndexDropStatement);
147build_schema_stmt!(sea_query::ForeignKeyCreateStatement);
148build_schema_stmt!(sea_query::ForeignKeyDropStatement);
149
150macro_rules! build_type_stmt {
151    ($stmt: ty) => {
152        impl StatementBuilder for $stmt {
153            fn build(&self, db_backend: &DbBackend) -> Statement {
154                let stmt = build_postgres_stmt!(self, db_backend);
155                Statement::from_string(*db_backend, stmt)
156            }
157
158            #[cfg(feature = "rbac")]
159            fn audit(&self) -> Result<QueryAccessAudit, AuditError> {
160                todo!("Audit not supported for {} yet", stringify!($stmt))
161            }
162        }
163    };
164}
165
166build_type_stmt!(sea_query::extension::postgres::TypeAlterStatement);
167build_type_stmt!(sea_query::extension::postgres::TypeCreateStatement);
168build_type_stmt!(sea_query::extension::postgres::TypeDropStatement);