1use compiler::{build_statement, build_substatement};
6use error::EloquentError;
7use std::fmt::Display;
8
9mod builders;
10mod checks;
11mod compiler;
12mod compilers;
13pub mod error;
15mod queries;
16mod query_builder;
17mod subqueries;
18mod subquery_builder;
19mod validator;
20
21pub struct QueryBuilder {
23 table: Option<String>,
24 selects: Vec<Select>,
25 inserts: Vec<Insert>,
26 updates: Vec<Update>,
27 delete: bool,
28 conditions: Vec<Condition>,
29 closures: Vec<(Logic, Vec<Condition>)>,
30 joins: Vec<Join>,
31 havings: Vec<Having>,
32 group_by: Vec<String>,
33 order_by: Vec<OrderColumn>,
34 limit: Option<u64>,
35 offset: Option<u64>,
36 enable_checks: bool,
37 paginate: Option<Paginate>,
38}
39
40pub struct SubqueryBuilder {
42 table: Option<String>,
43 selects: Vec<Select>,
44 conditions: Vec<Condition>,
45 joins: Vec<Join>,
46 havings: Vec<Having>,
47 group_by: Vec<String>,
48 order_by: Vec<OrderColumn>,
49 limit: Option<u64>,
50 offset: Option<u64>,
51}
52
53pub trait ToSql {
54 fn to_sql(&self) -> Result<String, EloquentError>;
55
56 fn is_subquery(&self) -> bool {
57 false
58 }
59}
60
61pub trait Columnable {
62 fn to_columns(&self) -> Vec<String>;
63}
64
65pub trait Selectable {
66 fn to_select_column(&self) -> String;
67}
68
69pub(crate) trait PerformChecks {
70 fn check(builder: &QueryBuilder) -> Result<(), EloquentError>;
71}
72
73#[allow(clippy::borrowed_box)]
74pub(crate) trait SqlBuilder {
75 fn build<'a>(
76 builder: &'a QueryBuilder,
77 sql: &mut String,
78 params: &mut Vec<&'a Box<(dyn ToSql + 'static)>>,
79 ) -> Result<String, EloquentError>;
80}
81
82#[derive(Debug, PartialEq)]
83pub(crate) enum Action {
84 Select,
85 Insert,
86 Update,
87 Delete,
88}
89
90struct Condition {
91 field: String,
92 operator: Operator,
93 logic: Logic,
94 values: Vec<Box<dyn ToSql>>,
95}
96
97struct Select {
98 column: String,
99 function: Option<Function>,
100 alias: Option<String>,
101}
102
103struct Insert {
104 column: String,
105 values: Vec<Box<dyn ToSql>>,
106}
107
108struct Update {
109 column: String,
110 value: Box<dyn ToSql>,
111}
112
113#[derive(PartialEq)]
114struct OrderColumn {
115 column: String,
116 order: Order,
117}
118
119struct Having {
120 conditions: Vec<Condition>,
121}
122
123#[derive(Debug, PartialEq)]
124enum Operator {
125 Equal,
126 NotEqual,
127 GreaterThan,
128 GreaterThanOrEqual,
129 LessThan,
130 LessThanOrEqual,
131 Between,
132 Like,
133 In,
134 NotIn,
135 IsNull,
136 IsNotNull,
137 Date,
138 Year,
139 Month,
140 Day,
141}
142
143#[derive(Debug, PartialEq)]
144enum Logic {
145 And,
146 Or,
147}
148
149#[derive(Debug, PartialEq, Eq, Hash)]
150enum Function {
151 Count,
152 Sum,
153 Avg,
154 Min,
155 Max,
156 Distinct,
157}
158
159struct Join {
160 table: String,
161 left_hand: String,
162 join_type: JoinType,
163 right_hand: String,
164}
165
166enum JoinType {
167 Inner,
168 Left,
169 Right,
170 Full,
171}
172
173#[derive(Debug, PartialEq)]
174pub(crate) enum Order {
175 Asc,
176 Desc,
177}
178
179pub(crate) struct Paginate {
180 column: String,
181 last_id: Option<Box<dyn ToSql>>,
182 per_page: u64,
183}
184
185impl Select {
186 fn format_column_name(&self) -> String {
187 let column = match &self.function {
188 Some(function) => match function {
189 Function::Distinct => format!("{} {}", function, self.column),
190 _ => format!("{}({})", function, self.column),
191 },
192 None => self.column.clone(),
193 };
194
195 if let Some(alias) = &self.alias {
196 format!("{} AS {}", column, alias)
197 } else {
198 column
199 }
200 }
201
202 fn format_column_name_without_alias(&self) -> String {
203 match &self.function {
204 Some(function) => match function {
205 Function::Distinct => format!("{} {}", function, self.column),
206 _ => format!("{}({})", function, self.column),
207 },
208 None => self.column.clone(),
209 }
210 }
211}
212
213impl Selectable for &str {
214 fn to_select_column(&self) -> String {
215 self.to_string()
216 }
217}
218
219impl Selectable for SubqueryBuilder {
220 fn to_select_column(&self) -> String {
221 self.to_sql().unwrap()
222 }
223}
224
225impl Condition {
226 fn new(field: &str, operator: Operator, logic: Logic, values: Vec<Box<dyn ToSql>>) -> Self {
227 Condition {
228 field: field.to_string(),
229 operator,
230 logic,
231 values,
232 }
233 }
234}
235
236impl ToSql for &str {
237 fn to_sql(&self) -> Result<String, EloquentError> {
238 Ok(format!("'{}'", self.replace('\'', "''")))
239 }
240}
241
242impl ToSql for String {
243 fn to_sql(&self) -> Result<String, EloquentError> {
244 Ok(format!("'{}'", self.replace('\'', "''")))
245 }
246}
247
248impl ToSql for &String {
249 fn to_sql(&self) -> Result<String, EloquentError> {
250 Ok(format!("'{}'", self.replace('\'', "''")))
251 }
252}
253
254impl ToSql for i32 {
255 fn to_sql(&self) -> Result<String, EloquentError> {
256 Ok(self.to_string())
257 }
258}
259
260impl ToSql for i64 {
261 fn to_sql(&self) -> Result<String, EloquentError> {
262 Ok(self.to_string())
263 }
264}
265
266impl ToSql for u32 {
267 fn to_sql(&self) -> Result<String, EloquentError> {
268 Ok(self.to_string())
269 }
270}
271
272impl ToSql for u64 {
273 fn to_sql(&self) -> Result<String, EloquentError> {
274 Ok(self.to_string())
275 }
276}
277
278impl ToSql for f32 {
279 fn to_sql(&self) -> Result<String, EloquentError> {
280 Ok(self.to_string())
281 }
282}
283
284impl ToSql for f64 {
285 fn to_sql(&self) -> Result<String, EloquentError> {
286 Ok(self.to_string())
287 }
288}
289
290impl ToSql for bool {
291 fn to_sql(&self) -> Result<String, EloquentError> {
292 Ok(self.to_string())
293 }
294}
295
296impl ToSql for QueryBuilder {
297 fn to_sql(&self) -> Result<String, EloquentError> {
298 build_statement(self)
299 }
300}
301
302impl ToSql for SubqueryBuilder {
303 fn to_sql(&self) -> Result<String, EloquentError> {
304 build_substatement(self)
305 }
306
307 fn is_subquery(&self) -> bool {
308 true
309 }
310}
311
312impl Columnable for &str {
313 fn to_columns(&self) -> Vec<String> {
314 vec![self.to_string()]
315 }
316}
317
318impl Columnable for Vec<&str> {
319 fn to_columns(&self) -> Vec<String> {
320 self.iter().map(|&s| s.to_string()).collect()
321 }
322}
323
324impl Display for Operator {
325 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
326 let operator = match self {
327 Operator::Equal => "=",
328 Operator::NotEqual => "!=",
329 Operator::GreaterThan => ">",
330 Operator::GreaterThanOrEqual => ">=",
331 Operator::LessThan => "<",
332 Operator::LessThanOrEqual => "<=",
333 Operator::Between => "BETWEEN",
334 Operator::Like => "LIKE",
335 Operator::In => "IN",
336 Operator::NotIn => "NOT IN",
337 Operator::IsNull => "IS NULL",
338 Operator::IsNotNull => "IS NOT NULL",
339 Operator::Date => "DATE",
340 Operator::Year => "YEAR",
341 Operator::Month => "MONTH",
342 Operator::Day => "DAY",
343 };
344
345 write!(f, "{}", operator)
346 }
347}
348
349impl Display for Order {
350 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
351 let order = match self {
352 Order::Asc => "ASC",
353 Order::Desc => "DESC",
354 };
355
356 write!(f, "{}", order)
357 }
358}
359
360impl Display for Function {
361 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
362 let function = match self {
363 Function::Count => "COUNT",
364 Function::Sum => "SUM",
365 Function::Avg => "AVG",
366 Function::Min => "MIN",
367 Function::Max => "MAX",
368 Function::Distinct => "DISTINCT",
369 };
370
371 write!(f, "{}", function)
372 }
373}
374
375impl Display for JoinType {
376 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
377 let join_type = match self {
378 JoinType::Inner => "JOIN",
379 JoinType::Left => "LEFT JOIN",
380 JoinType::Right => "RIGHT JOIN",
381 JoinType::Full => "FULL JOIN",
382 };
383
384 write!(f, "{}", join_type)
385 }
386}
387
388impl Condition {
389 fn format_sql(&self) -> String {
390 let values = self
391 .values
392 .iter()
393 .map(|v| v.to_sql().unwrap())
394 .collect::<Vec<String>>()
395 .join(", ");
396
397 match self.operator {
398 Operator::Between => format!(
399 "{} {} {} AND {}",
400 self.field,
401 self.operator,
402 values.split(", ").next().unwrap(),
403 values.split(", ").last().unwrap()
404 ),
405 Operator::In | Operator::NotIn => {
406 if self.values.iter().any(|v| v.is_subquery()) {
407 format!("{} {} {}", self.field, self.operator, values)
409 } else {
410 format!("{} {} ({})", self.field, self.operator, values)
411 }
412 }
413 Operator::IsNull | Operator::IsNotNull => format!("{} {}", self.field, self.operator),
414 Operator::Date | Operator::Year | Operator::Month | Operator::Day => {
415 format!("{}({}) = {}", self.operator, self.field, values)
416 }
417 _ => format!("{} {} {}", self.field, self.operator, values),
418 }
419 }
420}
421
422#[macro_export]
423macro_rules! eloquent_sql_row {
424 ($($key:expr => $value:expr),* $(,)?) => {
425 vec![
426 $(($key, Box::new($value) as Box<dyn $crate::ToSql>)),*
427 ]
428 };
429}