1use std::marker::PhantomData;
74use tracing::debug;
75
76use crate::error::QueryResult;
77use crate::filter::FilterValue;
78use crate::sql::DatabaseType;
79use crate::traits::{Model, QueryEngine};
80
81#[derive(Debug, Clone)]
83pub struct Sql {
84 parts: Vec<String>,
86 params: Vec<FilterValue>,
88 db_type: DatabaseType,
90}
91
92impl Sql {
93 pub fn new(sql: impl Into<String>) -> Self {
95 Self {
96 parts: vec![sql.into()],
97 params: Vec::new(),
98 db_type: DatabaseType::PostgreSQL,
99 }
100 }
101
102 pub fn empty() -> Self {
104 Self {
105 parts: Vec::new(),
106 params: Vec::new(),
107 db_type: DatabaseType::PostgreSQL,
108 }
109 }
110
111 pub fn with_db_type(mut self, db_type: DatabaseType) -> Self {
113 self.db_type = db_type;
114 self
115 }
116
117 pub fn push(mut self, sql: impl Into<String>) -> Self {
119 if let Some(last) = self.parts.last_mut() {
120 last.push_str(&sql.into());
121 } else {
122 self.parts.push(sql.into());
123 }
124 self
125 }
126
127 pub fn bind(mut self, value: impl Into<FilterValue>) -> Self {
129 let index = self.params.len() + 1;
130 let placeholder = self.db_type.placeholder(index);
131
132 if let Some(last) = self.parts.last_mut() {
133 last.push_str(&placeholder);
135 } else {
136 self.parts.push(placeholder.into_owned());
138 }
139
140 self.params.push(value.into());
141 self
142 }
143
144 pub fn bind_many(mut self, values: impl IntoIterator<Item = FilterValue>) -> Self {
146 for value in values {
147 self = self.bind(value);
148 }
149 self
150 }
151
152 pub fn push_if(self, condition: bool, sql: impl Into<String>) -> Self {
154 if condition { self.push(sql) } else { self }
155 }
156
157 pub fn bind_if(self, condition: bool, value: impl Into<FilterValue>) -> Self {
159 if condition { self.bind(value) } else { self }
160 }
161
162 pub fn push_bind(self, sql: impl Into<String>, value: impl Into<FilterValue>) -> Self {
164 self.push(sql).bind(value)
165 }
166
167 pub fn push_bind_if(
169 self,
170 condition: bool,
171 sql: impl Into<String>,
172 value: impl Into<FilterValue>,
173 ) -> Self {
174 if condition {
175 self.push(sql).bind(value)
176 } else {
177 self
178 }
179 }
180
181 pub fn separated(self, separator: &str) -> SeparatedSql {
183 SeparatedSql {
184 sql: self,
185 separator: separator.to_string(),
186 first: true,
187 }
188 }
189
190 pub fn build(self) -> (String, Vec<FilterValue>) {
192 let sql = self.parts.join("");
193 debug!(sql_len = sql.len(), param_count = self.params.len(), db_type = ?self.db_type, "Sql::build()");
194 (sql, self.params)
195 }
196
197 pub fn sql(&self) -> String {
199 self.parts.join("")
200 }
201
202 pub fn params(&self) -> &[FilterValue] {
204 &self.params
205 }
206
207 pub fn param_count(&self) -> usize {
209 self.params.len()
210 }
211
212 pub fn is_empty(&self) -> bool {
214 self.parts.is_empty() || self.parts.iter().all(|p| p.is_empty())
215 }
216}
217
218impl Default for Sql {
219 fn default() -> Self {
220 Self::empty()
221 }
222}
223
224impl std::fmt::Display for Sql {
225 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
226 write!(f, "{}", self.parts.join(""))
227 }
228}
229
230#[derive(Debug, Clone)]
232pub struct SeparatedSql {
233 sql: Sql,
234 separator: String,
235 first: bool,
236}
237
238impl SeparatedSql {
239 pub fn push(mut self, sql: impl Into<String>) -> Self {
241 if !self.first {
242 self.sql = self.sql.push(&self.separator);
243 }
244 self.sql = self.sql.push(sql);
245 self.first = false;
246 self
247 }
248
249 pub fn push_bind(mut self, sql: impl Into<String>, value: impl Into<FilterValue>) -> Self {
251 if !self.first {
252 self.sql = self.sql.push(&self.separator);
253 }
254 self.sql = self.sql.push(sql).bind(value);
255 self.first = false;
256 self
257 }
258
259 pub fn push_bind_if(
261 mut self,
262 condition: bool,
263 sql: impl Into<String>,
264 value: impl Into<FilterValue>,
265 ) -> Self {
266 if condition {
267 if !self.first {
268 self.sql = self.sql.push(&self.separator);
269 }
270 self.sql = self.sql.push(sql).bind(value);
271 self.first = false;
272 }
273 self
274 }
275
276 pub fn finish(self) -> Sql {
278 self.sql
279 }
280
281 pub fn build(self) -> (String, Vec<FilterValue>) {
283 self.sql.build()
284 }
285}
286
287#[derive(Debug)]
289pub struct RawQueryOperation<M, E>
290where
291 M: Model + Send + 'static,
292 E: QueryEngine,
293{
294 _model: PhantomData<M>,
295 engine: E,
296 sql: Sql,
297}
298
299impl<M, E> RawQueryOperation<M, E>
300where
301 M: Model + crate::row::FromRow + Send + 'static,
302 E: QueryEngine,
303{
304 pub fn new(engine: E, sql: Sql) -> Self {
306 Self {
307 _model: PhantomData,
308 engine,
309 sql,
310 }
311 }
312
313 pub async fn exec(self) -> QueryResult<Vec<M>> {
315 let (sql, params) = self.sql.build();
316 self.engine.query_many(&sql, params).await
317 }
318
319 pub async fn exec_one(self) -> QueryResult<M> {
321 let (sql, params) = self.sql.build();
322 self.engine.query_one(&sql, params).await
323 }
324
325 pub async fn exec_optional(self) -> QueryResult<Option<M>> {
327 let (sql, params) = self.sql.build();
328 self.engine.query_optional(&sql, params).await
329 }
330}
331
332#[derive(Debug)]
334pub struct RawExecuteOperation<E>
335where
336 E: QueryEngine,
337{
338 engine: E,
339 sql: Sql,
340}
341
342impl<E> RawExecuteOperation<E>
343where
344 E: QueryEngine,
345{
346 pub fn new(engine: E, sql: Sql) -> Self {
348 Self { engine, sql }
349 }
350
351 pub async fn exec(self) -> QueryResult<u64> {
353 let (sql, params) = self.sql.build();
354 self.engine.execute_raw(&sql, params).await
355 }
356}
357
358pub fn sql(query: impl Into<String>) -> Sql {
360 Sql::new(query)
361}
362
363pub fn sql_with_params(sql_str: impl Into<String>, params: Vec<FilterValue>) -> Sql {
367 let mut sql = Sql::new(sql_str);
368 sql.params = params;
369 sql
370}
371
372#[macro_export]
383macro_rules! raw_query {
384 ($sql:expr) => {
386 $crate::raw::Sql::new($sql)
387 };
388
389 ($sql:expr, $($params:expr),+ $(,)?) => {{
391 let parts: Vec<&str> = $sql.split("{}").collect();
392 let param_values: Vec<$crate::filter::FilterValue> = vec![
393 $($params.into()),+
394 ];
395
396 let mut sql = $crate::raw::Sql::empty();
397 let mut param_iter = param_values.into_iter();
398
399 for (i, part) in parts.iter().enumerate() {
401 if !part.is_empty() {
402 sql = sql.push(*part);
403 }
404 if i < parts.len() - 1 {
405 if let Some(param) = param_iter.next() {
406 sql = sql.bind(param);
407 }
408 }
409 }
410
411 sql
412 }};
413}
414
415#[cfg(test)]
416mod tests {
417 use super::*;
418
419 #[test]
420 fn test_sql_new() {
421 let sql = Sql::new("SELECT * FROM users");
422 assert_eq!(sql.sql(), "SELECT * FROM users");
423 assert!(sql.params().is_empty());
424 }
425
426 #[test]
427 fn test_sql_push() {
428 let sql = Sql::new("SELECT * FROM users").push(" WHERE id = 1");
429 assert_eq!(sql.sql(), "SELECT * FROM users WHERE id = 1");
430 }
431
432 #[test]
433 fn test_sql_bind() {
434 let sql = Sql::new("SELECT * FROM users WHERE id = ").bind(42i32);
435 let (query, params) = sql.build();
436 assert_eq!(query, "SELECT * FROM users WHERE id = $1");
437 assert_eq!(params.len(), 1);
438 }
439
440 #[test]
441 fn test_sql_multiple_binds() {
442 let sql = Sql::new("SELECT * FROM users WHERE id = ")
443 .bind(42i32)
444 .push(" AND name = ")
445 .bind("John".to_string());
446 let (query, params) = sql.build();
447 assert_eq!(query, "SELECT * FROM users WHERE id = $1 AND name = $2");
448 assert_eq!(params.len(), 2);
449 }
450
451 #[test]
452 fn test_sql_push_bind() {
453 let sql = Sql::new("SELECT * FROM users WHERE").push_bind(" id = ", 42i32);
454 let (query, params) = sql.build();
455 assert_eq!(query, "SELECT * FROM users WHERE id = $1");
456 assert_eq!(params.len(), 1);
457 }
458
459 #[test]
460 fn test_sql_push_if() {
461 let include_active = true;
462 let include_deleted = false;
463
464 let sql = Sql::new("SELECT * FROM users")
465 .push_if(include_active, " WHERE active = true")
466 .push_if(include_deleted, " AND deleted = false");
467
468 assert_eq!(sql.sql(), "SELECT * FROM users WHERE active = true");
469 }
470
471 #[test]
472 #[allow(clippy::unnecessary_literal_unwrap)]
473 fn test_sql_bind_if() {
474 let filter_id = Some(42i32);
475 let filter_name: Option<String> = None;
476
477 let sql = Sql::new("SELECT * FROM users WHERE 1=1")
478 .push_bind_if(filter_id.is_some(), " AND id = ", filter_id.unwrap_or(0))
479 .push_bind_if(filter_name.is_some(), " AND name = ", String::new());
480
481 let (query, params) = sql.build();
482 assert_eq!(query, "SELECT * FROM users WHERE 1=1 AND id = $1");
483 assert_eq!(params.len(), 1);
484 }
485
486 #[test]
487 fn test_sql_separated() {
488 let columns = vec!["id", "name", "email"];
489
490 let mut sep = Sql::new("SELECT ").separated(", ");
491
492 for col in columns {
493 sep = sep.push(col);
494 }
495
496 let sql = sep.finish().push(" FROM users");
497 assert_eq!(sql.sql(), "SELECT id, name, email FROM users");
498 }
499
500 #[test]
501 fn test_sql_separated_with_binds() {
502 let filters = vec![("id", 1i32), ("active", 1i32)];
503
504 let mut sep = Sql::new("SELECT * FROM users WHERE ").separated(" AND ");
505
506 for (col, val) in filters {
507 sep = sep.push_bind(format!("{} = ", col), val);
508 }
509
510 let (query, params) = sep.build();
511 assert_eq!(query, "SELECT * FROM users WHERE id = $1 AND active = $2");
512 assert_eq!(params.len(), 2);
513 }
514
515 #[test]
516 fn test_sql_mysql() {
517 let sql = Sql::new("SELECT * FROM users WHERE id = ")
518 .with_db_type(DatabaseType::MySQL)
519 .bind(42i32);
520 let (query, params) = sql.build();
521 assert_eq!(query, "SELECT * FROM users WHERE id = ?");
522 assert_eq!(params.len(), 1);
523 }
524
525 #[test]
526 fn test_sql_sqlite() {
527 let sql = Sql::new("SELECT * FROM users WHERE id = ")
528 .with_db_type(DatabaseType::SQLite)
529 .bind(42i32);
530 let (query, params) = sql.build();
531 assert_eq!(query, "SELECT * FROM users WHERE id = ?");
532 assert_eq!(params.len(), 1);
533 }
534
535 #[test]
536 fn test_sql_is_empty() {
537 assert!(Sql::empty().is_empty());
538 assert!(!Sql::new("SELECT 1").is_empty());
539 }
540
541 #[test]
542 fn test_sql_display() {
543 let sql = Sql::new("SELECT * FROM users WHERE id = ").bind(42i32);
544 assert_eq!(format!("{}", sql), "SELECT * FROM users WHERE id = $1");
545 }
546
547 #[test]
548 fn test_raw_query_macro_no_params() {
549 let sql = raw_query!("SELECT * FROM users");
550 assert_eq!(sql.sql(), "SELECT * FROM users");
551 assert!(sql.params().is_empty());
552 }
553
554 #[test]
555 fn test_raw_query_macro_with_params() {
556 let sql = raw_query!(
557 "SELECT * FROM users WHERE id = {} AND active = {}",
558 42i32,
559 true
560 );
561 let (query, params) = sql.build();
562 assert_eq!(query, "SELECT * FROM users WHERE id = $1 AND active = $2");
563 assert_eq!(params.len(), 2);
564 }
565
566 #[test]
567 fn test_raw_query_macro_string_params() {
568 let name = "John".to_string();
569 let sql = raw_query!("SELECT * FROM users WHERE name = {}", name);
570 let (query, params) = sql.build();
571 assert_eq!(query, "SELECT * FROM users WHERE name = $1");
572 assert_eq!(params.len(), 1);
573 }
574
575 #[test]
576 fn test_bind_many() {
577 let values: Vec<FilterValue> = vec![
578 FilterValue::Int(1),
579 FilterValue::Int(2),
580 FilterValue::Int(3),
581 ];
582
583 let sql = Sql::new("SELECT * FROM users WHERE id IN (")
584 .bind_many(values)
585 .push(")");
586
587 let (query, params) = sql.build();
588 assert_eq!(query, "SELECT * FROM users WHERE id IN ($1$2$3)");
589 assert_eq!(params.len(), 3);
590 }
591
592 #[test]
593 fn test_build_in_clause() {
594 let ids = vec![1, 2, 3];
595
596 let placeholders: Vec<String> = (1..=ids.len()).map(|i| format!("${}", i)).collect();
597
598 let sql = Sql::new(format!(
599 "SELECT * FROM users WHERE id IN ({})",
600 placeholders.join(", ")
601 ));
602
603 let params: Vec<FilterValue> = ids.into_iter().map(FilterValue::Int).collect();
604 let sql = sql_with_params(sql.sql(), params);
605
606 let (query, params) = sql.build();
607 assert_eq!(query, "SELECT * FROM users WHERE id IN ($1, $2, $3)");
608 assert_eq!(params.len(), 3);
609 }
610}