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 {
22    /// Method 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 = match self.db_backend {
70                    DbBackend::MySql => inject_parameters(&self.sql, &values.0, &MysqlQueryBuilder),
71                    DbBackend::Postgres => {
72                        inject_parameters(&self.sql, &values.0, &PostgresQueryBuilder)
73                    }
74                    DbBackend::Sqlite => {
75                        inject_parameters(&self.sql, &values.0, &SqliteQueryBuilder)
76                    }
77                };
78                write!(f, "{}", &string)
79            }
80            None => {
81                write!(f, "{}", &self.sql)
82            }
83        }
84    }
85}
86
87macro_rules! build_any_stmt {
88    ($stmt: expr, $db_backend: expr) => {
89        match $db_backend {
90            DbBackend::MySql => $stmt.build(MysqlQueryBuilder),
91            DbBackend::Postgres => $stmt.build(PostgresQueryBuilder),
92            DbBackend::Sqlite => $stmt.build(SqliteQueryBuilder),
93        }
94    };
95}
96
97macro_rules! build_postgres_stmt {
98    ($stmt: expr, $db_backend: expr) => {
99        match $db_backend {
100            DbBackend::Postgres => $stmt.to_string(PostgresQueryBuilder),
101            DbBackend::MySql | DbBackend::Sqlite => unimplemented!(),
102        }
103    };
104}
105
106macro_rules! build_query_stmt {
107    ($stmt: ty) => {
108        impl StatementBuilder for $stmt {
109            fn build(&self, db_backend: &DbBackend) -> Statement {
110                let stmt = build_any_stmt!(self, db_backend);
111                Statement::from_string_values_tuple(*db_backend, stmt)
112            }
113
114            #[cfg(feature = "rbac")]
115            fn audit(&self) -> Result<QueryAccessAudit, AuditError> {
116                AuditTrait::audit(self)
117            }
118        }
119    };
120}
121
122build_query_stmt!(sea_query::InsertStatement);
123build_query_stmt!(sea_query::SelectStatement);
124build_query_stmt!(sea_query::UpdateStatement);
125build_query_stmt!(sea_query::DeleteStatement);
126build_query_stmt!(sea_query::WithQuery);
127
128macro_rules! build_schema_stmt {
129    ($stmt: ty) => {
130        impl StatementBuilder for $stmt {
131            fn build(&self, db_backend: &DbBackend) -> Statement {
132                let stmt = build_any_stmt!(self, db_backend);
133                Statement::from_string(*db_backend, stmt)
134            }
135
136            #[cfg(feature = "rbac")]
137            fn audit(&self) -> Result<QueryAccessAudit, AuditError> {
138                todo!("Audit not supported for {} yet", stringify!($stmt))
139            }
140        }
141    };
142}
143
144build_schema_stmt!(sea_query::TableCreateStatement);
145build_schema_stmt!(sea_query::TableDropStatement);
146build_schema_stmt!(sea_query::TableAlterStatement);
147build_schema_stmt!(sea_query::TableRenameStatement);
148build_schema_stmt!(sea_query::TableTruncateStatement);
149build_schema_stmt!(sea_query::IndexCreateStatement);
150build_schema_stmt!(sea_query::IndexDropStatement);
151build_schema_stmt!(sea_query::ForeignKeyCreateStatement);
152build_schema_stmt!(sea_query::ForeignKeyDropStatement);
153
154macro_rules! build_type_stmt {
155    ($stmt: ty) => {
156        impl StatementBuilder for $stmt {
157            fn build(&self, db_backend: &DbBackend) -> Statement {
158                let stmt = build_postgres_stmt!(self, db_backend);
159                Statement::from_string(*db_backend, stmt)
160            }
161
162            #[cfg(feature = "rbac")]
163            fn audit(&self) -> Result<QueryAccessAudit, AuditError> {
164                Err(AuditError::UnsupportedQuery)
165            }
166        }
167    };
168}
169
170build_type_stmt!(sea_query::extension::postgres::TypeAlterStatement);
171build_type_stmt!(sea_query::extension::postgres::TypeCreateStatement);
172build_type_stmt!(sea_query::extension::postgres::TypeDropStatement);