1use crate::builder::{
9 ConflictAction, Cte, Having, Join, JoinCond, JoinKind, Method, Order, QueryBuilder,
10};
11use crate::dialect::{Dialect, UpsertStyle};
12use crate::ident::escape_identifier;
13use crate::value::Value;
14use crate::where_::{Conj, Predicate};
15
16struct Ctx {
23 sql: String,
24 binds: Vec<Value>,
25 quote: char,
26}
27
28impl Ctx {
29 fn placeholder<D: Dialect>(&mut self, val: Value) {
31 self.binds.push(val);
32 D::write_placeholder(&mut self.sql, self.binds.len());
33 }
34
35 fn esc(&self, ident: &str) -> String {
43 escape_identifier(ident, self.quote)
44 }
45
46 fn qualify(&self, db: Option<&str>, table: &str) -> String {
49 match db {
50 Some(d) => format!("{}.{}", self.esc(d), self.esc(table)),
51 None => self.esc(table),
52 }
53 }
54}
55
56pub fn compile<D: Dialect>(qb: &QueryBuilder<D>) -> (String, Vec<Value>) {
58 let mut ctx = Ctx {
59 sql: String::new(),
60 binds: Vec::new(),
61 quote: D::quote_char(),
62 };
63 compile_into::<D>(&mut ctx, qb);
64 (ctx.sql, ctx.binds)
65}
66
67fn compile_into<D: Dialect>(ctx: &mut Ctx, qb: &QueryBuilder<D>) {
71 let table = ctx.qualify(qb.db.as_deref(), &qb.table);
72
73 match qb.method {
74 Method::Select => {
75 write_ctes::<D>(ctx, &qb.ctes);
77 if !qb.distinct_on.is_empty() {
78 if !D::supports_distinct_on() {
79 panic!("DISTINCT ON requires PostgreSQL");
80 }
81 ctx.sql.push_str("SELECT DISTINCT ON (");
82 let cols: Vec<String> = qb.distinct_on.iter().map(|c| ctx.esc(c)).collect();
83 ctx.sql.push_str(&cols.join(", "));
84 ctx.sql.push_str(") ");
85 } else if qb.distinct {
86 ctx.sql.push_str("SELECT DISTINCT ");
87 } else {
88 ctx.sql.push_str("SELECT ");
89 }
90 write_select_list::<D>(ctx, qb);
91 ctx.sql.push_str(" FROM ");
92 ctx.sql.push_str(&table);
93 write_joins::<D>(ctx, &qb.joins, qb.db.as_deref());
94 write_wheres::<D>(ctx, &qb.wheres);
95 write_group_by(ctx, &qb.groups, qb.group_by_raw.as_ref());
96 write_having::<D>(ctx, &qb.havings);
97 write_order_by(ctx, &qb.orders, qb.order_by_raw.as_ref());
98 write_limit_offset::<D>(ctx, qb.limit, qb.offset);
99 write_unions::<D>(ctx, &qb.unions);
100 }
101 Method::Insert => {
102 if qb.set.is_empty() && qb.insert_rows.is_empty() {
103 panic!("insert() requires at least one column");
104 }
105
106 let mut single_rows: Vec<&(String, Value)> = qb.set.iter().collect();
110 single_rows.sort_by(|a, b| a.0.cmp(&b.0));
111 let sorted_cols: Vec<&str> = if !qb.insert_rows.is_empty() {
112 let mut cols: Vec<&str> =
113 qb.insert_rows[0].iter().map(|(k, _)| k.as_str()).collect();
114 cols.sort_unstable();
115 cols
116 } else {
117 single_rows.iter().map(|(k, _)| k.as_str()).collect()
118 };
119
120 let mysql_ignore = D::upsert_style() == UpsertStyle::OnDuplicateKey
123 && matches!(
124 qb.on_conflict.as_ref().map(|c| c.action),
125 Some(ConflictAction::DoNothing)
126 );
127 if mysql_ignore {
128 ctx.sql.push_str("INSERT IGNORE INTO ");
129 } else {
130 ctx.sql.push_str("INSERT INTO ");
131 }
132 ctx.sql.push_str(&table);
133 ctx.sql.push_str(" (");
134 let cols: Vec<String> = sorted_cols.iter().map(|k| ctx.esc(k)).collect();
135 ctx.sql.push_str(&cols.join(", "));
136 ctx.sql.push_str(") VALUES ");
137
138 if !qb.insert_rows.is_empty() {
139 for (ri, row) in qb.insert_rows.iter().enumerate() {
142 if ri > 0 {
143 ctx.sql.push_str(", ");
144 }
145 ctx.sql.push('(');
146 for (ci, col) in sorted_cols.iter().enumerate() {
147 if ci > 0 {
148 ctx.sql.push_str(", ");
149 }
150 let v = row
151 .iter()
152 .find(|(k, _)| k == col)
153 .map(|(_, v)| v.clone())
154 .unwrap_or(Value::Null);
155 ctx.placeholder::<D>(v);
156 }
157 ctx.sql.push(')');
158 }
159 } else {
160 ctx.sql.push('(');
163 for (i, (_, v)) in single_rows.iter().enumerate() {
164 if i > 0 {
165 ctx.sql.push_str(", ");
166 }
167 ctx.placeholder::<D>(v.clone());
168 }
169 ctx.sql.push(')');
170 }
171
172 if !mysql_ignore {
173 if let Some(oc) = &qb.on_conflict {
174 write_on_conflict::<D>(ctx, oc, &sorted_cols);
175 }
176 }
177 write_returning::<D>(ctx, &qb.returning);
178 }
179 Method::Update => {
180 if qb.set.is_empty() {
181 panic!("update() requires at least one column");
182 }
183 let mut rows: Vec<&(String, Value)> = qb.set.iter().collect();
184 rows.sort_by(|a, b| a.0.cmp(&b.0));
185 ctx.sql.push_str("UPDATE ");
186 ctx.sql.push_str(&table);
187 ctx.sql.push_str(" SET ");
188 for (i, (k, v)) in rows.iter().enumerate() {
189 if i > 0 {
190 ctx.sql.push_str(", ");
191 }
192 let col = ctx.esc(k);
193 ctx.sql.push_str(&col);
194 ctx.sql.push_str(" = ");
195 ctx.placeholder::<D>(v.clone());
196 }
197 write_wheres::<D>(ctx, &qb.wheres);
198 write_returning::<D>(ctx, &qb.returning);
199 }
200 Method::Delete => {
201 ctx.sql.push_str("DELETE FROM ");
202 ctx.sql.push_str(&table);
203 write_wheres::<D>(ctx, &qb.wheres);
204 write_returning::<D>(ctx, &qb.returning);
205 }
206 }
207}
208
209fn write_on_conflict<D: Dialect>(
213 ctx: &mut Ctx,
214 oc: &crate::builder::OnConflict,
215 inserted: &[&str],
216) {
217 match D::upsert_style() {
218 UpsertStyle::OnDuplicateKey => {
219 ctx.sql.push_str(" ON DUPLICATE KEY UPDATE ");
222 let sets: Vec<String> = inserted
223 .iter()
224 .map(|c| {
225 let e = ctx.esc(c);
226 format!("{e} = VALUES({e})")
227 })
228 .collect();
229 ctx.sql.push_str(&sets.join(", "));
230 }
231 UpsertStyle::OnConflict => {
232 let targets = &oc.targets;
233 let set_cols: Vec<&&str> = inserted
235 .iter()
236 .filter(|c| !targets.iter().any(|t| t == **c))
237 .collect();
238 let do_update = matches!(oc.action, ConflictAction::Merge)
239 && !targets.is_empty()
240 && !set_cols.is_empty();
241
242 ctx.sql.push_str(" ON CONFLICT");
243 if !targets.is_empty() {
244 ctx.sql.push_str(" (");
245 let cols: Vec<String> = targets.iter().map(|t| ctx.esc(t)).collect();
246 ctx.sql.push_str(&cols.join(", "));
247 ctx.sql.push(')');
248 }
249 if do_update {
250 ctx.sql.push_str(" DO UPDATE SET ");
251 let sets: Vec<String> = set_cols
252 .iter()
253 .map(|c| {
254 let e = ctx.esc(c);
255 format!("{e} = EXCLUDED.{e}")
258 })
259 .collect();
260 ctx.sql.push_str(&sets.join(", "));
261 } else {
262 ctx.sql.push_str(" DO NOTHING");
263 }
264 }
265 }
266}
267
268fn write_returning<D: Dialect>(ctx: &mut Ctx, cols: &[String]) {
271 if !D::supports_returning() || cols.is_empty() {
272 return;
273 }
274 ctx.sql.push_str(" RETURNING ");
275 let parts: Vec<String> = cols
276 .iter()
277 .map(|c| if c == "*" { "*".to_owned() } else { ctx.esc(c) })
278 .collect();
279 ctx.sql.push_str(&parts.join(", "));
280}
281
282fn write_group_by(ctx: &mut Ctx, groups: &[String], raw: Option<&(String, Vec<Value>)>) {
286 if groups.is_empty() && raw.is_none() {
287 return;
288 }
289 ctx.sql.push_str(" GROUP BY ");
290 let cols: Vec<String> = groups.iter().map(|c| ctx.esc(c)).collect();
291 ctx.sql.push_str(&cols.join(", "));
292 if let Some((sql, binds)) = raw {
293 if !groups.is_empty() {
294 ctx.sql.push_str(", ");
295 }
296 ctx.sql.push_str(sql);
298 ctx.binds.extend(binds.iter().cloned());
299 }
300}
301
302fn write_select_list<D: Dialect>(ctx: &mut Ctx, qb: &QueryBuilder<D>) {
309 if qb.select_cols.is_empty() && qb.select_raw.is_empty() && qb.select_subqueries.is_empty() {
310 ctx.sql.push('*');
311 return;
312 }
313 let mut wrote_any = false;
314 for c in &qb.select_cols {
315 if wrote_any {
316 ctx.sql.push_str(", ");
317 }
318 let e = ctx.esc(c);
319 ctx.sql.push_str(&e);
320 wrote_any = true;
321 }
322 for (sql, binds) in &qb.select_raw {
323 if wrote_any {
324 ctx.sql.push_str(", ");
325 }
326 ctx.sql.push_str(sql);
328 ctx.binds.extend(binds.iter().cloned());
329 wrote_any = true;
330 }
331 for (alias, sub) in &qb.select_subqueries {
332 if wrote_any {
333 ctx.sql.push_str(", ");
334 }
335 ctx.sql.push('(');
336 compile_into::<D>(ctx, sub);
337 ctx.sql.push_str(") AS ");
338 let a = ctx.esc(alias);
339 ctx.sql.push_str(&a);
340 wrote_any = true;
341 }
342}
343
344fn write_joins<D: Dialect>(ctx: &mut Ctx, joins: &[Join], db: Option<&str>) {
348 for j in joins {
349 let kw = match j.kind {
350 JoinKind::Inner => " INNER JOIN ",
351 JoinKind::Left => " LEFT JOIN ",
352 JoinKind::Right => " RIGHT JOIN ",
353 JoinKind::FullOuter => " FULL OUTER JOIN ",
354 JoinKind::Cross => " CROSS JOIN ",
355 };
356 ctx.sql.push_str(kw);
357 let table = ctx.qualify(db, &j.table);
358 ctx.sql.push_str(&table);
359 if j.on.is_empty() {
360 continue;
361 }
362 ctx.sql.push_str(" ON ");
363 for (i, cond) in j.on.iter().enumerate() {
364 if i > 0 {
365 ctx.sql.push_str(" AND ");
366 }
367 match cond {
368 JoinCond::On(c, op, c2) => {
369 let l = ctx.esc(c);
370 let r = ctx.esc(c2);
371 ctx.sql.push_str(&l);
372 ctx.sql.push(' ');
373 ctx.sql.push_str(op);
374 ctx.sql.push(' ');
375 ctx.sql.push_str(&r);
376 }
377 JoinCond::OnVal(c, op, v) => {
378 let l = ctx.esc(c);
379 ctx.sql.push_str(&l);
380 ctx.sql.push(' ');
381 ctx.sql.push_str(op);
382 ctx.sql.push(' ');
383 ctx.placeholder::<D>(v.clone());
384 }
385 JoinCond::OnRaw(sql, binds) => {
386 ctx.sql.push_str(sql);
388 ctx.binds.extend(binds.iter().cloned());
389 }
390 }
391 }
392 }
393}
394
395fn write_having<D: Dialect>(ctx: &mut Ctx, havings: &[Having]) {
397 if havings.is_empty() {
398 return;
399 }
400 ctx.sql.push_str(" HAVING ");
401 for (i, h) in havings.iter().enumerate() {
402 if i > 0 {
403 ctx.sql.push_str(" AND ");
404 }
405 match h {
406 Having::Col { col, op, val } => {
407 let c = ctx.esc(col);
408 ctx.sql.push_str(&c);
409 ctx.sql.push(' ');
410 ctx.sql.push_str(op);
411 ctx.sql.push(' ');
412 ctx.placeholder::<D>(val.clone());
413 }
414 Having::Raw { sql, binds } => {
415 ctx.sql.push_str(sql);
417 ctx.binds.extend(binds.iter().cloned());
418 }
419 }
420 }
421}
422
423fn write_ctes<D: Dialect>(ctx: &mut Ctx, ctes: &[Cte<D>]) {
428 if ctes.is_empty() {
429 return;
430 }
431 ctx.sql.push_str("WITH ");
432 if ctes.iter().any(|c| c.recursive) {
433 ctx.sql.push_str("RECURSIVE ");
434 }
435 for (i, cte) in ctes.iter().enumerate() {
436 if i > 0 {
437 ctx.sql.push_str(", ");
438 }
439 let name = ctx.esc(&cte.name);
440 ctx.sql.push_str(&name);
441 ctx.sql.push_str(" AS (");
442 compile_into::<D>(ctx, &cte.query);
443 ctx.sql.push(')');
444 }
445 ctx.sql.push(' ');
446}
447
448fn write_unions<D: Dialect>(ctx: &mut Ctx, unions: &[(bool, QueryBuilder<D>)]) {
450 for (all, arm) in unions {
451 ctx.sql
452 .push_str(if *all { " UNION ALL " } else { " UNION " });
453 compile_into::<D>(ctx, arm);
454 }
455}
456
457fn write_order_by(ctx: &mut Ctx, orders: &[(String, Order)], raw: Option<&(String, Vec<Value>)>) {
461 if orders.is_empty() && raw.is_none() {
462 return;
463 }
464 ctx.sql.push_str(" ORDER BY ");
465 let cols: Vec<String> = orders
466 .iter()
467 .map(|(c, o)| {
468 let dir = match o {
469 Order::Asc => "ASC",
470 Order::Desc => "DESC",
471 };
472 format!("{} {}", ctx.esc(c), dir)
473 })
474 .collect();
475 ctx.sql.push_str(&cols.join(", "));
476 if let Some((sql, binds)) = raw {
477 if !orders.is_empty() {
478 ctx.sql.push_str(", ");
479 }
480 ctx.sql.push_str(sql);
482 ctx.binds.extend(binds.iter().cloned());
483 }
484}
485
486fn write_limit_offset<D: Dialect>(ctx: &mut Ctx, limit: Option<i64>, offset: Option<i64>) {
491 if offset.is_some() && limit.is_none() {
492 panic!("offset(...) requires limit(...)");
493 }
494 if let Some(n) = limit {
495 ctx.sql.push_str(" LIMIT ");
496 ctx.placeholder::<D>(Value::I64(n));
497 }
498 if let Some(n) = offset {
499 ctx.sql.push_str(" OFFSET ");
500 ctx.placeholder::<D>(Value::I64(n));
501 }
502}
503
504fn is_omitted<D: Dialect>(p: &Predicate<D>) -> bool {
508 matches!(p, Predicate::Group { preds, .. } if preds.is_empty())
509}
510
511fn write_wheres<D: Dialect>(ctx: &mut Ctx, wheres: &[Predicate<D>]) {
512 if wheres.iter().all(is_omitted) {
514 return;
515 }
516 ctx.sql.push_str(" WHERE ");
517 write_clause_list::<D>(ctx, wheres);
518}
519
520fn write_clause_list<D: Dialect>(ctx: &mut Ctx, preds: &[Predicate<D>]) {
525 let mut wrote_any = false;
526 for p in preds.iter() {
527 if is_omitted(p) {
528 continue;
529 }
530 if wrote_any {
531 let sep = match p {
532 Predicate::Group {
533 outer_conj: Conj::Or,
534 ..
535 } => " OR ",
536 _ => " AND ",
537 };
538 ctx.sql.push_str(sep);
539 }
540 write_pred::<D>(ctx, p);
541 wrote_any = true;
542 }
543}
544
545fn write_pred<D: Dialect>(ctx: &mut Ctx, pred: &Predicate<D>) {
546 match pred {
547 Predicate::Binary { col, op, val } => {
548 let col = ctx.esc(col);
549 ctx.sql.push_str(&col);
550 ctx.sql.push(' ');
551 ctx.sql.push_str(op);
552 ctx.sql.push(' ');
553 ctx.placeholder::<D>(val.clone());
554 }
555 Predicate::In { col, neg, vals } => {
556 if vals.is_empty() {
557 ctx.sql.push_str(if *neg { "1 = 1" } else { "1 = 0" });
559 return;
560 }
561 let col = ctx.esc(col);
562 ctx.sql.push_str(&col);
563 ctx.sql.push_str(if *neg { " NOT IN (" } else { " IN (" });
564 for (i, v) in vals.iter().enumerate() {
565 if i > 0 {
566 ctx.sql.push_str(", ");
567 }
568 ctx.placeholder::<D>(v.clone());
569 }
570 ctx.sql.push(')');
571 }
572 Predicate::Null { col, neg } => {
573 let col = ctx.esc(col);
574 ctx.sql.push_str(&col);
575 ctx.sql
576 .push_str(if *neg { " IS NOT NULL" } else { " IS NULL" });
577 }
578 Predicate::Between { col, lo, hi } => {
579 let col = ctx.esc(col);
580 ctx.sql.push_str(&col);
581 ctx.sql.push_str(" BETWEEN ");
582 ctx.placeholder::<D>(lo.clone());
583 ctx.sql.push_str(" AND ");
584 ctx.placeholder::<D>(hi.clone());
585 }
586 Predicate::ILike { col, val } => {
587 let col = ctx.esc(col);
588 if D::ilike_is_native() {
589 ctx.sql.push_str(&col);
591 ctx.sql.push_str(" ILIKE ");
592 ctx.placeholder::<D>(val.clone());
593 } else {
594 ctx.sql.push_str("LOWER(");
596 ctx.sql.push_str(&col);
597 ctx.sql.push_str(") LIKE LOWER(");
598 ctx.placeholder::<D>(val.clone());
599 ctx.sql.push(')');
600 }
601 }
602 Predicate::JsonContains { col, val } => {
603 let col = ctx.esc(col);
605 ctx.sql.push_str(&col);
606 ctx.sql.push_str(" @> ");
607 ctx.placeholder::<D>(val.clone());
608 }
609 Predicate::Raw { sql, binds } => {
610 ctx.sql.push_str(sql);
612 ctx.binds.extend(binds.iter().cloned());
613 }
614 Predicate::Group {
615 outer_conj: _,
616 preds,
617 } => {
618 ctx.sql.push('(');
632 write_clause_list::<D>(ctx, preds);
633 ctx.sql.push(')');
634 }
635 Predicate::Column { lhs, op, rhs } => {
636 let l = ctx.esc(lhs);
637 let r = ctx.esc(rhs);
638 ctx.sql.push_str(&l);
639 ctx.sql.push(' ');
640 ctx.sql.push_str(op);
641 ctx.sql.push(' ');
642 ctx.sql.push_str(&r);
643 }
644 Predicate::Exists { neg, sub } => {
645 ctx.sql
646 .push_str(if *neg { "NOT EXISTS (" } else { "EXISTS (" });
647 compile_into::<D>(ctx, sub);
648 ctx.sql.push(')');
649 }
650 Predicate::InSubquery { col, neg, sub } => {
651 let col = ctx.esc(col);
652 ctx.sql.push_str(&col);
653 ctx.sql.push_str(if *neg { " NOT IN (" } else { " IN (" });
654 compile_into::<D>(ctx, sub);
655 ctx.sql.push(')');
656 }
657 }
658}