1#[macro_use]
2mod fmt;
3use fmt::ToSql;
4
5mod column;
6use column::ColumnAlias;
7
8mod cte;
9
10mod delim;
11use delim::{Comma, Delimited, Period};
12
13mod flavor;
14use flavor::Flavor;
15
16mod ident;
17use ident::Ident;
18
19mod params;
20pub use params::Placeholder;
21
22mod column_def;
24mod expr;
25mod name;
26mod statement;
27mod ty;
28mod value;
29
30use crate::stmt::Statement;
31
32use toasty_core::{
33 driver::operation::{IsolationLevel, Transaction},
34 schema::db::{self, Index, Table},
35};
36
37#[derive(Debug)]
39pub struct Serializer<'a> {
40 schema: &'a db::Schema,
42
43 flavor: Flavor,
46}
47
48struct Formatter<'a> {
49 serializer: &'a Serializer<'a>,
51
52 dst: &'a mut String,
54
55 depth: usize,
58
59 alias: bool,
61
62 in_insert: bool,
66
67 arg_positions: Vec<usize>,
71}
72
73pub type ExprContext<'a> = toasty_core::stmt::ExprContext<'a, db::Schema>;
75
76impl<'a> Serializer<'a> {
77 pub fn serialize(&self, stmt: &Statement) -> String {
85 self.serialize_with_arg_order(stmt).0
86 }
87
88 pub fn serialize_with_arg_order(&self, stmt: &Statement) -> (String, Vec<usize>) {
96 let mut ret = String::new();
97
98 let mut fmt = Formatter {
99 serializer: self,
100 dst: &mut ret,
101 depth: 0,
102 alias: false,
103 in_insert: false,
104 arg_positions: Vec::new(),
105 };
106
107 let cx = ExprContext::new(self.schema);
108
109 stmt.to_sql(&cx, &mut fmt);
110
111 let arg_positions = fmt.arg_positions;
112 ret.push(';');
113 (ret, arg_positions)
114 }
115
116 pub fn serialize_transaction(&self, op: &Transaction) -> String {
121 let mut ret = String::new();
122
123 let mut f = Formatter {
124 serializer: self,
125 dst: &mut ret,
126 depth: 0,
127 alias: false,
128 in_insert: false,
129 arg_positions: Vec::new(),
130 };
131
132 let cx = ExprContext::new(self.schema);
133
134 match op {
135 Transaction::Start {
136 isolation,
137 read_only,
138 } => fmt!(
139 &cx,
140 &mut f,
141 self.serialize_transaction_start(*isolation, *read_only)
142 ),
143 Transaction::Commit => fmt!(&cx, &mut f, "COMMIT"),
144 Transaction::Rollback => fmt!(&cx, &mut f, "ROLLBACK"),
145 Transaction::Savepoint(name) => {
146 fmt!(&cx, &mut f, "SAVEPOINT " Ident(name))
147 }
148 Transaction::ReleaseSavepoint(name) => {
149 fmt!(&cx, &mut f, "RELEASE SAVEPOINT " Ident(name))
150 }
151 Transaction::RollbackToSavepoint(name) => {
152 fmt!(&cx, &mut f, "ROLLBACK TO SAVEPOINT " Ident(name))
153 }
154 };
155
156 ret.push(';');
157 ret
158 }
159
160 fn serialize_transaction_start(
161 &self,
162 isolation: Option<IsolationLevel>,
163 read_only: bool,
164 ) -> String {
165 fn isolation_level_str(level: IsolationLevel) -> &'static str {
166 match level {
167 IsolationLevel::ReadUncommitted => "READ UNCOMMITTED",
168 IsolationLevel::ReadCommitted => "READ COMMITTED",
169 IsolationLevel::RepeatableRead => "REPEATABLE READ",
170 IsolationLevel::Serializable => "SERIALIZABLE",
171 }
172 }
173
174 match self.flavor {
175 Flavor::Mysql => {
176 let mut sql = String::new();
177 if let Some(level) = isolation {
178 sql.push_str("SET TRANSACTION ISOLATION LEVEL ");
179 sql.push_str(isolation_level_str(level));
180 sql.push_str("; ");
181 }
182 sql.push_str("START TRANSACTION");
183 if read_only {
184 sql.push_str(" READ ONLY");
185 }
186 sql
187 }
188 Flavor::Postgresql => {
189 let mut sql = String::from("BEGIN");
190 if let Some(level) = isolation {
191 sql.push_str(" ISOLATION LEVEL ");
192 sql.push_str(isolation_level_str(level));
193 }
194 if read_only {
195 sql.push_str(" READ ONLY");
196 }
197 sql
198 }
199 Flavor::Sqlite => {
200 "BEGIN".to_string()
202 }
203 }
204 }
205
206 fn table(&self, id: impl Into<db::TableId>) -> &'a Table {
207 self.schema.table(id.into())
208 }
209
210 fn index(&self, id: impl Into<db::IndexId>) -> &'a Index {
211 self.schema.index(id.into())
212 }
213
214 fn table_name(&self, id: impl Into<db::TableId>) -> Ident<&str> {
215 let table = self.schema.table(id.into());
216 Ident(&table.name)
217 }
218
219 fn column_name(&self, id: impl Into<db::ColumnId>) -> Ident<&str> {
220 let column = self.schema.column(id.into());
221 Ident(&column.name)
222 }
223}