1use anyhow::Result;
4use sqlx::{PgPool, FromRow, postgres::PgRow, Postgres};
5use crate::query_builder::QueryValue;
6
7pub struct RawQueryBuilder {
9 sql: String,
10 params: Vec<QueryValue>,
11 param_count: usize,
12}
13
14impl RawQueryBuilder {
15 pub fn new(sql: &str) -> Self {
17 Self {
18 sql: sql.to_string(),
19 params: Vec::new(),
20 param_count: 1,
21 }
22 }
23
24 pub fn bind<T: Into<QueryValue>>(mut self, value: T) -> Self {
26 self.params.push(value.into());
27 self.param_count += 1;
28 self
29 }
30
31 pub fn bind_many<T: Into<QueryValue>>(mut self, values: Vec<T>) -> Self {
33 for value in values {
34 self.params.push(value.into());
35 self.param_count += 1;
36 }
37 self
38 }
39
40 pub fn build(self) -> (String, Vec<QueryValue>) {
42 let mut sql = self.sql;
43
44 for (i, param) in self.params.iter().enumerate() {
48 let placeholder = format!("${}", i + 1);
49 if let Some(pos) = sql.find(&placeholder) {
50 let replacement = match param {
51 QueryValue::Text(s) => format!("'{}'", s.replace('\'', "''")),
52 QueryValue::Integer(n) => n.to_string(),
53 QueryValue::Float(f) => f.to_string(),
54 QueryValue::Boolean(b) => b.to_string(),
55 QueryValue::Uuid(u) => format!("'{}'", u),
56 QueryValue::Timestamp(ts) => format!("'{}'", ts),
57 QueryValue::Null => "NULL".to_string(),
58 };
59 sql.replace_range(pos..pos + placeholder.len(), &replacement);
60 }
61 }
62
63 (sql, self.params)
64 }
65
66 pub async fn execute<T>(self, pool: &PgPool) -> Result<Vec<T>>
68 where
69 T: for<'r> FromRow<'r, PgRow> + Send + Unpin,
70 {
71 let (sql, params) = self.build_parameterized();
72
73 let mut query = sqlx::query_as::<Postgres, T>(&sql);
74
75 for param in params {
77 query = match param {
78 QueryValue::Text(val) => query.bind(val),
79 QueryValue::Integer(val) => query.bind(val),
80 QueryValue::Float(val) => query.bind(val),
81 QueryValue::Boolean(val) => query.bind(val),
82 QueryValue::Uuid(val) => query.bind(val),
83 QueryValue::Timestamp(val) => query.bind(val),
84 QueryValue::Null => query.bind::<Option<String>>(None),
85 };
86 }
87
88 let results = query.fetch_all(pool).await?;
89 Ok(results)
90 }
91
92 pub async fn execute_first<T>(self, pool: &PgPool) -> Result<Option<T>>
94 where
95 T: for<'r> FromRow<'r, PgRow> + Send + Unpin,
96 {
97 let (sql, params) = self.build_parameterized();
98
99 let mut query = sqlx::query_as::<Postgres, T>(&sql);
100
101 for param in params {
103 query = match param {
104 QueryValue::Text(val) => query.bind(val),
105 QueryValue::Integer(val) => query.bind(val),
106 QueryValue::Float(val) => query.bind(val),
107 QueryValue::Boolean(val) => query.bind(val),
108 QueryValue::Uuid(val) => query.bind(val),
109 QueryValue::Timestamp(val) => query.bind(val),
110 QueryValue::Null => query.bind::<Option<String>>(None),
111 };
112 }
113
114 let result = query.fetch_optional(pool).await?;
115 Ok(result)
116 }
117
118 pub async fn execute_raw(self, pool: &PgPool) -> Result<u64> {
120 let (sql, params) = self.build_parameterized();
121
122 let mut query = sqlx::query(&sql);
123
124 for param in params {
126 query = match param {
127 QueryValue::Text(val) => query.bind(val),
128 QueryValue::Integer(val) => query.bind(val),
129 QueryValue::Float(val) => query.bind(val),
130 QueryValue::Boolean(val) => query.bind(val),
131 QueryValue::Uuid(val) => query.bind(val),
132 QueryValue::Timestamp(val) => query.bind(val),
133 QueryValue::Null => query.bind::<Option<String>>(None),
134 };
135 }
136
137 let result = query.execute(pool).await?;
138 Ok(result.rows_affected())
139 }
140
141 fn build_parameterized(self) -> (String, Vec<QueryValue>) {
143 (self.sql, self.params)
144 }
145}
146
147pub struct AdvancedQueryBuilder {
149 base_table: String,
150 fields: Vec<String>,
151 joins: Vec<JoinClause>,
152 conditions: Vec<(String, QueryValue)>,
153 group_by: Vec<String>,
154 having: Vec<(String, QueryValue)>,
155 order_by: Vec<String>,
156 limit: Option<u32>,
157 offset: Option<u32>,
158 ctes: Vec<CteClause>,
159 window_functions: Vec<WindowFunction>,
160 next_param_id: usize,
161}
162
163#[derive(Debug, Clone)]
165pub struct JoinClause {
166 pub join_type: JoinType,
167 pub table: String,
168 pub on_condition: String,
169 pub alias: Option<String>,
170}
171
172#[derive(Debug, Clone)]
174pub enum JoinType {
175 Inner,
176 Left,
177 Right,
178 Full,
179 Cross,
180}
181
182#[derive(Debug, Clone)]
184pub struct CteClause {
185 pub name: String,
186 pub query: String,
187}
188
189#[derive(Debug, Clone)]
191pub struct WindowFunction {
192 pub expression: String,
193 pub alias: String,
194 pub partition_by: Vec<String>,
195 pub order_by: Vec<String>,
196 pub frame: Option<String>,
197}
198
199impl AdvancedQueryBuilder {
200 pub fn new(base_table: &str) -> Self {
202 Self {
203 base_table: base_table.to_string(),
204 fields: vec!["*".to_string()],
205 joins: Vec::new(),
206 conditions: Vec::new(),
207 group_by: Vec::new(),
208 having: Vec::new(),
209 order_by: Vec::new(),
210 limit: None,
211 offset: None,
212 ctes: Vec::new(),
213 window_functions: Vec::new(),
214 next_param_id: 1,
215 }
216 }
217
218 pub fn select(mut self, fields: &[&str]) -> Self {
220 self.fields = fields.iter().map(|f| f.to_string()).collect();
221 self
222 }
223
224 pub fn join(mut self, join_type: JoinType, table: &str, on_condition: &str) -> Self {
226 self.joins.push(JoinClause {
227 join_type,
228 table: table.to_string(),
229 on_condition: on_condition.to_string(),
230 alias: None,
231 });
232 self
233 }
234
235 pub fn join_alias(mut self, join_type: JoinType, table: &str, alias: &str, on_condition: &str) -> Self {
237 self.joins.push(JoinClause {
238 join_type,
239 table: table.to_string(),
240 on_condition: on_condition.to_string(),
241 alias: Some(alias.to_string()),
242 });
243 self
244 }
245
246 pub fn where_raw(mut self, condition: &str, param: QueryValue) -> Self {
248 self.conditions.push((condition.to_string(), param));
249 self.next_param_id += 1;
250 self
251 }
252
253 pub fn group_by(mut self, fields: &[&str]) -> Self {
255 self.group_by = fields.iter().map(|f| f.to_string()).collect();
256 self
257 }
258
259 pub fn having(mut self, condition: &str, param: QueryValue) -> Self {
261 self.having.push((condition.to_string(), param));
262 self.next_param_id += 1;
263 self
264 }
265
266 pub fn with_cte(mut self, name: &str, query: &str) -> Self {
268 self.ctes.push(CteClause {
269 name: name.to_string(),
270 query: query.to_string(),
271 });
272 self
273 }
274
275 pub fn window_fn(
277 mut self,
278 expression: &str,
279 alias: &str,
280 partition_by: &[&str],
281 order_by: &[&str]
282 ) -> Self {
283 self.window_functions.push(WindowFunction {
284 expression: expression.to_string(),
285 alias: alias.to_string(),
286 partition_by: partition_by.iter().map(|f| f.to_string()).collect(),
287 order_by: order_by.iter().map(|f| f.to_string()).collect(),
288 frame: None,
289 });
290 self
291 }
292
293 pub fn window_fn_with_frame(
295 mut self,
296 expression: &str,
297 alias: &str,
298 partition_by: &[&str],
299 order_by: &[&str],
300 frame: &str
301 ) -> Self {
302 self.window_functions.push(WindowFunction {
303 expression: expression.to_string(),
304 alias: alias.to_string(),
305 partition_by: partition_by.iter().map(|f| f.to_string()).collect(),
306 order_by: order_by.iter().map(|f| f.to_string()).collect(),
307 frame: Some(frame.to_string()),
308 });
309 self
310 }
311
312 pub fn order_by(mut self, field: &str, direction: &str) -> Self {
314 self.order_by.push(format!("{} {}", field, direction));
315 self
316 }
317
318 pub fn limit(mut self, limit: u32) -> Self {
320 self.limit = Some(limit);
321 self
322 }
323
324 pub fn offset(mut self, offset: u32) -> Self {
326 self.offset = Some(offset);
327 self
328 }
329
330 pub fn build_sql(&self) -> (String, Vec<QueryValue>) {
332 let mut sql = String::new();
333 let mut params = Vec::new();
334
335 if !self.ctes.is_empty() {
337 sql.push_str("WITH ");
338 let cte_strings: Vec<String> = self.ctes.iter()
339 .map(|cte| format!("{} AS ({})", cte.name, cte.query))
340 .collect();
341 sql.push_str(&cte_strings.join(", "));
342 sql.push(' ');
343 }
344
345 sql.push_str("SELECT ");
347
348 let mut all_fields = self.fields.clone();
350 for wf in &self.window_functions {
351 let mut wf_expr = wf.expression.clone();
352
353 if wf_expr.contains("OVER") {
355 all_fields.push(format!("{} AS {}", wf_expr, wf.alias));
357 } else {
358 if !wf.partition_by.is_empty() {
360 wf_expr = format!("{} OVER (PARTITION BY {}", wf_expr, wf.partition_by.join(", "));
361 if !wf.order_by.is_empty() {
362 wf_expr = format!("{} ORDER BY {}", wf_expr, wf.order_by.join(", "));
363 }
364
365 if let Some(frame) = &wf.frame {
367 wf_expr = format!("{} {})", wf_expr, frame);
368 } else {
369 wf_expr = format!("{})", wf_expr);
370 }
371 } else if !wf.order_by.is_empty() {
372 wf_expr = format!("{} OVER (ORDER BY {}", wf_expr, wf.order_by.join(", "));
373 if let Some(frame) = &wf.frame {
374 wf_expr = format!("{} {})", wf_expr, frame);
375 } else {
376 wf_expr = format!("{})", wf_expr);
377 }
378 }
379
380 all_fields.push(format!("{} AS {}", wf_expr, wf.alias));
381 }
382 }
383
384 sql.push_str(&all_fields.join(", "));
385 sql.push_str(" FROM ");
386 sql.push_str(&self.base_table);
387
388 for join in &self.joins {
390 let join_str = match join.join_type {
391 JoinType::Inner => "INNER JOIN",
392 JoinType::Left => "LEFT JOIN",
393 JoinType::Right => "RIGHT JOIN",
394 JoinType::Full => "FULL JOIN",
395 JoinType::Cross => "CROSS JOIN",
396 };
397
398 sql.push(' ');
399 sql.push_str(join_str);
400 sql.push(' ');
401 sql.push_str(&join.table);
402
403 if let Some(alias) = &join.alias {
404 sql.push_str(" AS ");
405 sql.push_str(alias);
406 }
407
408 sql.push_str(" ON ");
409 sql.push_str(&join.on_condition);
410 }
411
412 if !self.conditions.is_empty() {
414 sql.push_str(" WHERE ");
415 let condition_strings: Vec<String> = self.conditions.iter()
416 .map(|(cond, _)| cond.clone())
417 .collect();
418 sql.push_str(&condition_strings.join(" AND "));
419
420 for (_, param) in &self.conditions {
422 params.push(param.clone());
423 }
424 }
425
426 if !self.group_by.is_empty() {
428 sql.push_str(" GROUP BY ");
429 sql.push_str(&self.group_by.join(", "));
430 }
431
432 if !self.having.is_empty() {
434 sql.push_str(" HAVING ");
435 let having_strings: Vec<String> = self.having.iter()
436 .map(|(cond, _)| cond.clone())
437 .collect();
438 sql.push_str(&having_strings.join(" AND "));
439
440 for (_, param) in &self.having {
442 params.push(param.clone());
443 }
444 }
445
446 if !self.order_by.is_empty() {
448 sql.push_str(" ORDER BY ");
449 sql.push_str(&self.order_by.join(", "));
450 }
451
452 if let Some(limit) = self.limit {
454 sql.push_str(&format!(" LIMIT {}", limit));
455 }
456 if let Some(offset) = self.offset {
457 sql.push_str(&format!(" OFFSET {}", offset));
458 }
459
460 (sql, params)
461 }
462
463 pub async fn execute<T>(self, pool: &PgPool) -> Result<Vec<T>>
465 where
466 T: for<'r> FromRow<'r, PgRow> + Send + Unpin,
467 {
468 let (sql, params) = self.build_sql();
469
470 let mut query = sqlx::query_as::<Postgres, T>(&sql);
471
472 for param in params {
474 query = match param {
475 QueryValue::Text(val) => query.bind(val),
476 QueryValue::Integer(val) => query.bind(val),
477 QueryValue::Float(val) => query.bind(val),
478 QueryValue::Boolean(val) => query.bind(val),
479 QueryValue::Uuid(val) => query.bind(val),
480 QueryValue::Timestamp(val) => query.bind(val),
481 QueryValue::Null => query.bind::<Option<String>>(None),
482 };
483 }
484
485 let results = query.fetch_all(pool).await?;
486 Ok(results)
487 }
488
489 pub async fn execute_first<T>(self, pool: &PgPool) -> Result<Option<T>>
491 where
492 T: for<'r> FromRow<'r, PgRow> + Send + Unpin,
493 {
494 let (sql, params) = self.build_sql();
495
496 let mut query = sqlx::query_as::<Postgres, T>(&sql);
497
498 for param in params {
500 query = match param {
501 QueryValue::Text(val) => query.bind(val),
502 QueryValue::Integer(val) => query.bind(val),
503 QueryValue::Float(val) => query.bind(val),
504 QueryValue::Boolean(val) => query.bind(val),
505 QueryValue::Uuid(val) => query.bind(val),
506 QueryValue::Timestamp(val) => query.bind(val),
507 QueryValue::Null => query.bind::<Option<String>>(None),
508 };
509 }
510
511 let result = query.fetch_optional(pool).await?;
512 Ok(result)
513 }
514}
515
516pub struct RawQuery;
518
519impl RawQuery {
520 pub async fn scalar<T>(pool: &PgPool, sql: &str, params: Vec<QueryValue>) -> Result<T>
522 where
523 T: for<'r> sqlx::Decode<'r, sqlx::Postgres> + sqlx::Type<sqlx::Postgres> + Send + Unpin,
524 {
525 let mut query = sqlx::query_scalar::<Postgres, T>(sql);
526
527 for param in params {
528 query = match param {
529 QueryValue::Text(val) => query.bind(val),
530 QueryValue::Integer(val) => query.bind(val),
531 QueryValue::Float(val) => query.bind(val),
532 QueryValue::Boolean(val) => query.bind(val),
533 QueryValue::Uuid(val) => query.bind(val),
534 QueryValue::Timestamp(val) => query.bind(val),
535 QueryValue::Null => query.bind::<Option<String>>(None),
536 };
537 }
538
539 let result = query.fetch_one(pool).await?;
540 Ok(result)
541 }
542
543 pub async fn many<T>(pool: &PgPool, sql: &str, params: Vec<QueryValue>) -> Result<Vec<T>>
545 where
546 T: for<'r> sqlx::Decode<'r, sqlx::Postgres> + sqlx::Type<sqlx::Postgres> + Send + Unpin,
547 {
548 let mut query = sqlx::query_scalar::<Postgres, T>(sql);
549
550 for param in params {
551 query = match param {
552 QueryValue::Text(val) => query.bind(val),
553 QueryValue::Integer(val) => query.bind(val),
554 QueryValue::Float(val) => query.bind(val),
555 QueryValue::Boolean(val) => query.bind(val),
556 QueryValue::Uuid(val) => query.bind(val),
557 QueryValue::Timestamp(val) => query.bind(val),
558 QueryValue::Null => query.bind::<Option<String>>(None),
559 };
560 }
561
562 let result = query.fetch_all(pool).await?;
563 Ok(result)
564 }
565}