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 + 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 fn test_sql_bind_if() {
473 let filter_id = Some(42i32);
474 let filter_name: Option<String> = None;
475
476 let sql = Sql::new("SELECT * FROM users WHERE 1=1")
477 .push_bind_if(filter_id.is_some(), " AND id = ", filter_id.unwrap_or(0))
478 .push_bind_if(filter_name.is_some(), " AND name = ", "".to_string());
479
480 let (query, params) = sql.build();
481 assert_eq!(query, "SELECT * FROM users WHERE 1=1 AND id = $1");
482 assert_eq!(params.len(), 1);
483 }
484
485 #[test]
486 fn test_sql_separated() {
487 let columns = vec!["id", "name", "email"];
488
489 let mut sep = Sql::new("SELECT ").separated(", ");
490
491 for col in columns {
492 sep = sep.push(col);
493 }
494
495 let sql = sep.finish().push(" FROM users");
496 assert_eq!(sql.sql(), "SELECT id, name, email FROM users");
497 }
498
499 #[test]
500 fn test_sql_separated_with_binds() {
501 let filters = vec![("id", 1i32), ("active", 1i32)];
502
503 let mut sep = Sql::new("SELECT * FROM users WHERE ").separated(" AND ");
504
505 for (col, val) in filters {
506 sep = sep.push_bind(format!("{} = ", col), val);
507 }
508
509 let (query, params) = sep.build();
510 assert_eq!(query, "SELECT * FROM users WHERE id = $1 AND active = $2");
511 assert_eq!(params.len(), 2);
512 }
513
514 #[test]
515 fn test_sql_mysql() {
516 let sql = Sql::new("SELECT * FROM users WHERE id = ")
517 .with_db_type(DatabaseType::MySQL)
518 .bind(42i32);
519 let (query, params) = sql.build();
520 assert_eq!(query, "SELECT * FROM users WHERE id = ?");
521 assert_eq!(params.len(), 1);
522 }
523
524 #[test]
525 fn test_sql_sqlite() {
526 let sql = Sql::new("SELECT * FROM users WHERE id = ")
527 .with_db_type(DatabaseType::SQLite)
528 .bind(42i32);
529 let (query, params) = sql.build();
530 assert_eq!(query, "SELECT * FROM users WHERE id = ?");
531 assert_eq!(params.len(), 1);
532 }
533
534 #[test]
535 fn test_sql_is_empty() {
536 assert!(Sql::empty().is_empty());
537 assert!(!Sql::new("SELECT 1").is_empty());
538 }
539
540 #[test]
541 fn test_sql_display() {
542 let sql = Sql::new("SELECT * FROM users WHERE id = ").bind(42i32);
543 assert_eq!(format!("{}", sql), "SELECT * FROM users WHERE id = $1");
544 }
545
546 #[test]
547 fn test_raw_query_macro_no_params() {
548 let sql = raw_query!("SELECT * FROM users");
549 assert_eq!(sql.sql(), "SELECT * FROM users");
550 assert!(sql.params().is_empty());
551 }
552
553 #[test]
554 fn test_raw_query_macro_with_params() {
555 let sql = raw_query!(
556 "SELECT * FROM users WHERE id = {} AND active = {}",
557 42i32,
558 true
559 );
560 let (query, params) = sql.build();
561 assert_eq!(query, "SELECT * FROM users WHERE id = $1 AND active = $2");
562 assert_eq!(params.len(), 2);
563 }
564
565 #[test]
566 fn test_raw_query_macro_string_params() {
567 let name = "John".to_string();
568 let sql = raw_query!("SELECT * FROM users WHERE name = {}", name);
569 let (query, params) = sql.build();
570 assert_eq!(query, "SELECT * FROM users WHERE name = $1");
571 assert_eq!(params.len(), 1);
572 }
573
574 #[test]
575 fn test_bind_many() {
576 let values: Vec<FilterValue> = vec![
577 FilterValue::Int(1),
578 FilterValue::Int(2),
579 FilterValue::Int(3),
580 ];
581
582 let sql = Sql::new("SELECT * FROM users WHERE id IN (")
583 .bind_many(values)
584 .push(")");
585
586 let (query, params) = sql.build();
587 assert_eq!(query, "SELECT * FROM users WHERE id IN ($1$2$3)");
588 assert_eq!(params.len(), 3);
589 }
590
591 #[test]
592 fn test_build_in_clause() {
593 let ids = vec![1, 2, 3];
594
595 let placeholders: Vec<String> = (1..=ids.len()).map(|i| format!("${}", i)).collect();
596
597 let sql = Sql::new(format!(
598 "SELECT * FROM users WHERE id IN ({})",
599 placeholders.join(", ")
600 ));
601
602 let params: Vec<FilterValue> = ids.into_iter().map(FilterValue::Int).collect();
603 let sql = sql_with_params(sql.sql(), params);
604
605 let (query, params) = sql.build();
606 assert_eq!(query, "SELECT * FROM users WHERE id IN ($1, $2, $3)");
607 assert_eq!(params.len(), 3);
608 }
609}