halo_space/
select.rs

1//! SelectBuilder: build SELECT statements with composable clauses.
2
3use crate::args::Args;
4use crate::cond::{ArgsRef, Cond};
5use crate::cte::CTEBuilder;
6use crate::flavor::Flavor;
7use crate::injection::{Injection, InjectionMarker};
8use crate::macros::{IntoStrings, collect_into_strings};
9use crate::modifiers::{Arg, Builder};
10use crate::string_builder::StringBuilder;
11use crate::where_clause::{WhereClause, WhereClauseBuilder, WhereClauseRef};
12use std::cell::RefCell;
13use std::ops::Deref;
14use std::rc::Rc;
15
16const SELECT_MARKER_INIT: InjectionMarker = 0;
17const SELECT_MARKER_AFTER_WITH: InjectionMarker = 1;
18const SELECT_MARKER_AFTER_SELECT: InjectionMarker = 2;
19const SELECT_MARKER_AFTER_FROM: InjectionMarker = 3;
20const SELECT_MARKER_AFTER_JOIN: InjectionMarker = 4;
21const SELECT_MARKER_AFTER_WHERE: InjectionMarker = 5;
22const SELECT_MARKER_AFTER_GROUP_BY: InjectionMarker = 6;
23const SELECT_MARKER_AFTER_ORDER_BY: InjectionMarker = 7;
24const SELECT_MARKER_AFTER_LIMIT: InjectionMarker = 8;
25const SELECT_MARKER_AFTER_FOR: InjectionMarker = 9;
26
27/// JoinOption variants for explicit join keywords.
28#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29pub enum JoinOption {
30    FullJoin,
31    FullOuterJoin,
32    InnerJoin,
33    LeftJoin,
34    LeftOuterJoin,
35    RightJoin,
36    RightOuterJoin,
37}
38
39impl JoinOption {
40    fn as_str(self) -> &'static str {
41        match self {
42            Self::FullJoin => "FULL",
43            Self::FullOuterJoin => "FULL OUTER",
44            Self::InnerJoin => "INNER",
45            Self::LeftJoin => "LEFT",
46            Self::LeftOuterJoin => "LEFT OUTER",
47            Self::RightJoin => "RIGHT",
48            Self::RightOuterJoin => "RIGHT OUTER",
49        }
50    }
51}
52
53#[derive(Debug)]
54pub struct SelectBuilder {
55    args: ArgsRef,
56    cond: Cond,
57
58    distinct: bool,
59    tables: Vec<String>,
60    select_cols: Vec<String>,
61
62    join_options: Vec<Option<JoinOption>>,
63    join_tables: Vec<String>,
64    join_exprs: Vec<Vec<String>>,
65
66    where_clause: Option<WhereClauseRef>,
67    where_var: Option<String>,
68    cte_var: Option<String>,
69    cte: Option<CTEBuilder>,
70
71    having_exprs: Vec<String>,
72    group_by_cols: Vec<String>,
73    order_by_cols: Vec<String>,
74    order: Option<&'static str>,
75    limit_var: Option<String>,
76    offset_var: Option<String>,
77    for_what: Option<&'static str>,
78
79    injection: Injection,
80    marker: InjectionMarker,
81}
82
83impl Deref for SelectBuilder {
84    type Target = Cond;
85    fn deref(&self) -> &Self::Target {
86        &self.cond
87    }
88}
89
90impl SelectBuilder {
91    pub fn new() -> Self {
92        let args = Rc::new(RefCell::new(Args::default()));
93        let cond = Cond::with_args(args.clone());
94        Self {
95            args,
96            cond,
97            distinct: false,
98            tables: Vec::new(),
99            select_cols: Vec::new(),
100            join_options: Vec::new(),
101            join_tables: Vec::new(),
102            join_exprs: Vec::new(),
103            where_clause: None,
104            where_var: None,
105            cte_var: None,
106            cte: None,
107            having_exprs: Vec::new(),
108            group_by_cols: Vec::new(),
109            order_by_cols: Vec::new(),
110            order: None,
111            limit_var: None,
112            offset_var: None,
113            for_what: None,
114            injection: Injection::new(),
115            marker: SELECT_MARKER_INIT,
116        }
117    }
118
119    pub fn set_flavor(&mut self, flavor: Flavor) -> Flavor {
120        let mut a = self.args.borrow_mut();
121        let old = a.flavor;
122        a.flavor = flavor;
123        old
124    }
125
126    pub fn flavor(&self) -> Flavor {
127        self.args.borrow().flavor
128    }
129
130    pub fn with(&mut self, cte: &CTEBuilder) -> &mut Self {
131        let placeholder_builder = cte.clone();
132        let ph = self.var(Arg::Builder(Box::new(cte.clone())));
133        self.cte = Some(placeholder_builder);
134        self.cte_var = Some(ph);
135        self.marker = SELECT_MARKER_AFTER_WITH;
136        self
137    }
138
139    fn table_names(&self) -> Vec<String> {
140        let cte_tables = self
141            .cte
142            .as_ref()
143            .map(|cte| cte.table_names_for_from())
144            .unwrap_or_default();
145
146        if self.tables.is_empty() {
147            return cte_tables;
148        }
149
150        if cte_tables.is_empty() {
151            return self.tables.clone();
152        }
153
154        let mut out = Vec::with_capacity(self.tables.len() + cte_tables.len());
155        out.extend(self.tables.clone());
156        out.extend(cte_tables);
157        out
158    }
159
160    /// Returns the current WhereClause (can be shared across builders).
161    pub fn where_clause(&self) -> Option<WhereClauseRef> {
162        self.where_clause.clone()
163    }
164
165    /// Set or share a WhereClause.
166    ///
167    /// - `None` clears the WHERE clause.
168    /// - `Some(wc)` binds the WhereClause to this builder and ensures placeholders point to the right builder.
169    pub fn set_where_clause(&mut self, wc: Option<WhereClauseRef>) -> &mut Self {
170        match wc {
171            None => {
172                self.where_clause = None;
173                self.where_var = None;
174            }
175            Some(wc) => {
176                if let Some(ph) = &self.where_var {
177                    self.args.borrow_mut().replace(
178                        ph,
179                        Arg::Builder(Box::new(WhereClauseBuilder::new(wc.clone()))),
180                    );
181                } else {
182                    let ph = self.var(Arg::Builder(Box::new(WhereClauseBuilder::new(wc.clone()))));
183                    self.where_var = Some(ph);
184                }
185                self.where_clause = Some(wc);
186            }
187        }
188        self
189    }
190
191    pub fn clear_where_clause(&mut self) -> &mut Self {
192        self.set_where_clause(None)
193    }
194
195    /// AddWhereExpr: append expressions to the WhereClause with an explicit ArgsRef.
196    pub fn add_where_expr<T>(&mut self, args: ArgsRef, exprs: T) -> &mut Self
197    where
198        T: IntoStrings,
199    {
200        let exprs = collect_into_strings(exprs);
201        if exprs.is_empty() || exprs.iter().all(|s| s.is_empty()) {
202            return self;
203        }
204
205        if self.where_clause.is_none() {
206            let wc = WhereClause::new();
207            let ph = self.var(Arg::Builder(Box::new(WhereClauseBuilder::new(wc.clone()))));
208            self.where_clause = Some(wc);
209            self.where_var = Some(ph);
210        }
211        let wc = self.where_clause.as_ref().unwrap().clone();
212        wc.borrow_mut().add_where_expr(args, exprs);
213        self.marker = SELECT_MARKER_AFTER_WHERE;
214        self
215    }
216
217    pub fn clone_builder(&self) -> Self {
218        let old_args = self.args.borrow().clone();
219        let args = Rc::new(RefCell::new(old_args));
220        let cond = Cond::with_args(args.clone());
221
222        let mut cloned = Self {
223            args,
224            cond,
225            distinct: self.distinct,
226            tables: self.tables.clone(),
227            select_cols: self.select_cols.clone(),
228            join_options: self.join_options.clone(),
229            join_tables: self.join_tables.clone(),
230            join_exprs: self.join_exprs.clone(),
231            where_clause: self.where_clause.clone(),
232            where_var: self.where_var.clone(),
233            cte_var: self.cte_var.clone(),
234            cte: self.cte.clone(),
235            having_exprs: self.having_exprs.clone(),
236            group_by_cols: self.group_by_cols.clone(),
237            order_by_cols: self.order_by_cols.clone(),
238            order: self.order,
239            limit_var: self.limit_var.clone(),
240            offset_var: self.offset_var.clone(),
241            for_what: self.for_what,
242            injection: self.injection.clone(),
243            marker: self.marker,
244        };
245
246        // Deep clone WhereClause and fix Builder placeholder in args to point to the new WhereClause
247        if let (Some(wc), Some(ph)) = (&self.where_clause, &self.where_var) {
248            let new_wc = Rc::new(RefCell::new(wc.borrow().clone()));
249            cloned.where_clause = Some(new_wc.clone());
250            cloned
251                .args
252                .borrow_mut()
253                .replace(ph, Arg::Builder(Box::new(WhereClauseBuilder::new(new_wc))));
254        }
255
256        if let (Some(cte), Some(ph)) = (&self.cte, &self.cte_var) {
257            let new_cte = cte.clone();
258            let new_cte_for_field = new_cte.clone();
259            cloned.cte = Some(new_cte_for_field);
260            cloned
261                .args
262                .borrow_mut()
263                .replace(ph, Arg::Builder(Box::new(new_cte)));
264        }
265
266        cloned
267    }
268
269    pub fn build(&self) -> (String, Vec<Arg>) {
270        Builder::build(self)
271    }
272
273    fn var(&self, v: impl Into<Arg>) -> String {
274        self.args.borrow_mut().add(v)
275    }
276
277    pub fn select<T>(&mut self, cols: T) -> &mut Self
278    where
279        T: IntoStrings,
280    {
281        self.select_cols = collect_into_strings(cols);
282        self.marker = SELECT_MARKER_AFTER_SELECT;
283        self
284    }
285
286    pub fn select_more<T>(&mut self, cols: T) -> &mut Self
287    where
288        T: IntoStrings,
289    {
290        self.select_cols.extend(collect_into_strings(cols));
291        self.marker = SELECT_MARKER_AFTER_SELECT;
292        self
293    }
294
295    pub fn distinct(&mut self) -> &mut Self {
296        self.distinct = true;
297        self.marker = SELECT_MARKER_AFTER_SELECT;
298        self
299    }
300
301    pub fn from<T>(&mut self, tables: T) -> &mut Self
302    where
303        T: IntoStrings,
304    {
305        self.tables = collect_into_strings(tables);
306        self.marker = SELECT_MARKER_AFTER_FROM;
307        self
308    }
309
310    pub fn join(&mut self, table: impl Into<String>, on_expr: impl IntoStrings) -> &mut Self {
311        self.join_with_option(None, table, on_expr)
312    }
313
314    pub fn join_with_option(
315        &mut self,
316        option: Option<JoinOption>,
317        table: impl Into<String>,
318        on_expr: impl IntoStrings,
319    ) -> &mut Self {
320        self.join_options.push(option);
321        self.join_tables.push(table.into());
322        self.join_exprs.push(collect_into_strings(on_expr));
323        self.marker = SELECT_MARKER_AFTER_JOIN;
324        self
325    }
326
327    pub fn where_<T>(&mut self, and_expr: T) -> &mut Self
328    where
329        T: IntoStrings,
330    {
331        let exprs = collect_into_strings(and_expr);
332        if exprs.is_empty() || exprs.iter().all(|s| s.is_empty()) {
333            return self;
334        }
335
336        if self.where_clause.is_none() {
337            let wc = WhereClause::new();
338            let ph = self.var(Arg::Builder(Box::new(WhereClauseBuilder::new(wc.clone()))));
339            self.where_clause = Some(wc);
340            self.where_var = Some(ph);
341        }
342
343        let wc = self.where_clause.as_ref().unwrap().clone();
344        wc.borrow_mut().add_where_expr(self.args.clone(), exprs);
345        self.marker = SELECT_MARKER_AFTER_WHERE;
346        self
347    }
348
349    pub fn add_where_clause(&mut self, other: &WhereClause) -> &mut Self {
350        if self.where_clause.is_none() {
351            let wc = WhereClause::new();
352            let ph = self.var(Arg::Builder(Box::new(WhereClauseBuilder::new(wc.clone()))));
353            self.where_clause = Some(wc);
354            self.where_var = Some(ph);
355        }
356        self.where_clause
357            .as_ref()
358            .unwrap()
359            .borrow_mut()
360            .add_where_clause(other);
361        self
362    }
363
364    pub fn add_where_clause_ref(&mut self, other: &WhereClauseRef) -> &mut Self {
365        if self.where_clause.is_none() {
366            let wc = WhereClause::new();
367            let ph = self.var(Arg::Builder(Box::new(WhereClauseBuilder::new(wc.clone()))));
368            self.where_clause = Some(wc);
369            self.where_var = Some(ph);
370        }
371        self.where_clause
372            .as_ref()
373            .unwrap()
374            .borrow_mut()
375            .add_where_clause(&other.borrow());
376        self
377    }
378
379    pub fn having<T>(&mut self, and_expr: T) -> &mut Self
380    where
381        T: IntoStrings,
382    {
383        self.having_exprs.extend(collect_into_strings(and_expr));
384        self.marker = SELECT_MARKER_AFTER_GROUP_BY;
385        self
386    }
387
388    pub fn group_by<T>(&mut self, cols: T) -> &mut Self
389    where
390        T: IntoStrings,
391    {
392        self.group_by_cols.extend(collect_into_strings(cols));
393        self.marker = SELECT_MARKER_AFTER_GROUP_BY;
394        self
395    }
396
397    pub fn order_by<T>(&mut self, cols: T) -> &mut Self
398    where
399        T: IntoStrings,
400    {
401        self.order_by_cols.extend(collect_into_strings(cols));
402        self.marker = SELECT_MARKER_AFTER_ORDER_BY;
403        self
404    }
405
406    pub fn order_by_asc(&mut self, col: impl Into<String>) -> &mut Self {
407        self.order_by_cols.push(format!("{} ASC", col.into()));
408        self.marker = SELECT_MARKER_AFTER_ORDER_BY;
409        self
410    }
411
412    pub fn order_by_desc(&mut self, col: impl Into<String>) -> &mut Self {
413        self.order_by_cols.push(format!("{} DESC", col.into()));
414        self.marker = SELECT_MARKER_AFTER_ORDER_BY;
415        self
416    }
417
418    pub fn asc(&mut self) -> &mut Self {
419        self.order = Some("ASC");
420        self.marker = SELECT_MARKER_AFTER_ORDER_BY;
421        self
422    }
423
424    pub fn desc(&mut self) -> &mut Self {
425        self.order = Some("DESC");
426        self.marker = SELECT_MARKER_AFTER_ORDER_BY;
427        self
428    }
429
430    pub fn limit(&mut self, limit: i64) -> &mut Self {
431        if limit < 0 {
432            self.limit_var = None;
433            return self;
434        }
435        self.limit_var = Some(self.var(limit));
436        self.marker = SELECT_MARKER_AFTER_LIMIT;
437        self
438    }
439
440    pub fn offset(&mut self, offset: i64) -> &mut Self {
441        if offset < 0 {
442            self.offset_var = None;
443            return self;
444        }
445        self.offset_var = Some(self.var(offset));
446        self.marker = SELECT_MARKER_AFTER_LIMIT;
447        self
448    }
449
450    pub fn for_update(&mut self) -> &mut Self {
451        self.for_what = Some("UPDATE");
452        self.marker = SELECT_MARKER_AFTER_FOR;
453        self
454    }
455
456    pub fn for_share(&mut self) -> &mut Self {
457        self.for_what = Some("SHARE");
458        self.marker = SELECT_MARKER_AFTER_FOR;
459        self
460    }
461
462    pub fn as_(&self, name: &str, alias: &str) -> String {
463        format!("{name} AS {alias}")
464    }
465
466    pub fn builder_as(&self, builder: impl Builder + 'static, alias: &str) -> String {
467        format!(
468            "({}) AS {}",
469            self.var(Arg::Builder(Box::new(builder))),
470            alias
471        )
472    }
473
474    pub fn sql(&mut self, sql: impl Into<String>) -> &mut Self {
475        self.injection.sql(self.marker, sql);
476        self
477    }
478}
479
480impl Clone for SelectBuilder {
481    fn clone(&self) -> Self {
482        self.clone_builder()
483    }
484}
485
486impl Default for SelectBuilder {
487    fn default() -> Self {
488        Self::new()
489    }
490}
491
492impl Builder for SelectBuilder {
493    fn build_with_flavor(&self, flavor: Flavor, initial_arg: &[Arg]) -> (String, Vec<Arg>) {
494        let mut buf = StringBuilder::new();
495        write_injection(&mut buf, &self.injection, SELECT_MARKER_INIT);
496
497        if let Some(ph) = &self.cte_var {
498            buf.write_leading(ph);
499            write_injection(&mut buf, &self.injection, SELECT_MARKER_AFTER_WITH);
500        }
501
502        if !self.select_cols.is_empty() {
503            buf.write_leading("SELECT");
504            if self.distinct {
505                buf.write_str(" DISTINCT");
506            }
507            buf.write_str(" ");
508            buf.write_str(&self.select_cols.join(", "));
509        }
510        write_injection(&mut buf, &self.injection, SELECT_MARKER_AFTER_SELECT);
511
512        let table_names = self.table_names();
513        if !table_names.is_empty() {
514            buf.write_leading("FROM");
515            buf.write_str(" ");
516            buf.write_str(&table_names.join(", "));
517        }
518        write_injection(&mut buf, &self.injection, SELECT_MARKER_AFTER_FROM);
519
520        for i in 0..self.join_tables.len() {
521            if let Some(opt) = self.join_options[i] {
522                buf.write_leading(opt.as_str());
523            }
524            buf.write_leading("JOIN");
525            buf.write_str(" ");
526            buf.write_str(&self.join_tables[i]);
527
528            let on = self.join_exprs[i]
529                .iter()
530                .filter(|s| !s.is_empty())
531                .cloned()
532                .collect::<Vec<_>>();
533            if !on.is_empty() {
534                buf.write_str(" ON ");
535                buf.write_str(&on.join(" AND "));
536            }
537        }
538        if !self.join_tables.is_empty() {
539            write_injection(&mut buf, &self.injection, SELECT_MARKER_AFTER_JOIN);
540        }
541
542        if let Some(ph) = &self.where_var {
543            buf.write_leading(ph);
544            write_injection(&mut buf, &self.injection, SELECT_MARKER_AFTER_WHERE);
545        }
546
547        if !self.group_by_cols.is_empty() {
548            buf.write_leading("GROUP BY");
549            buf.write_str(" ");
550            buf.write_str(&self.group_by_cols.join(", "));
551            let having = self
552                .having_exprs
553                .iter()
554                .filter(|s| !s.is_empty())
555                .cloned()
556                .collect::<Vec<_>>();
557            if !having.is_empty() {
558                buf.write_str(" HAVING ");
559                buf.write_str(&having.join(" AND "));
560            }
561            write_injection(&mut buf, &self.injection, SELECT_MARKER_AFTER_GROUP_BY);
562        }
563
564        if !self.order_by_cols.is_empty() {
565            buf.write_leading("ORDER BY");
566            buf.write_str(" ");
567            buf.write_str(&self.order_by_cols.join(", "));
568            if let Some(order) = self.order {
569                buf.write_str(" ");
570                buf.write_str(order);
571            }
572            write_injection(&mut buf, &self.injection, SELECT_MARKER_AFTER_ORDER_BY);
573        }
574
575        // LIMIT/OFFSET rules follow each flavor's expected behavior
576        match flavor {
577            Flavor::MySQL | Flavor::SQLite | Flavor::ClickHouse => {
578                if let Some(lim) = &self.limit_var {
579                    buf.write_leading("LIMIT");
580                    buf.write_str(" ");
581                    buf.write_str(lim);
582                    if let Some(off) = &self.offset_var {
583                        buf.write_leading("OFFSET");
584                        buf.write_str(" ");
585                        buf.write_str(off);
586                    }
587                }
588            }
589            Flavor::CQL => {
590                if let Some(lim) = &self.limit_var {
591                    buf.write_leading("LIMIT");
592                    buf.write_str(" ");
593                    buf.write_str(lim);
594                }
595            }
596            Flavor::PostgreSQL => {
597                if let Some(lim) = &self.limit_var {
598                    buf.write_leading("LIMIT");
599                    buf.write_str(" ");
600                    buf.write_str(lim);
601                }
602                if let Some(off) = &self.offset_var {
603                    buf.write_leading("OFFSET");
604                    buf.write_str(" ");
605                    buf.write_str(off);
606                }
607            }
608            Flavor::Presto => {
609                if let Some(off) = &self.offset_var {
610                    buf.write_leading("OFFSET");
611                    buf.write_str(" ");
612                    buf.write_str(off);
613                }
614                if let Some(lim) = &self.limit_var {
615                    buf.write_leading("LIMIT");
616                    buf.write_str(" ");
617                    buf.write_str(lim);
618                }
619            }
620            Flavor::SQLServer | Flavor::Oracle => {
621                if self.order_by_cols.is_empty()
622                    && (self.limit_var.is_some() || self.offset_var.is_some())
623                    && flavor == Flavor::SQLServer
624                {
625                    buf.write_leading("ORDER BY 1");
626                }
627
628                if let Some(off) = &self.offset_var {
629                    buf.write_leading("OFFSET");
630                    buf.write_str(" ");
631                    buf.write_str(off);
632                    buf.write_str(" ROWS");
633                }
634
635                if let Some(lim) = &self.limit_var {
636                    if self.offset_var.is_none() {
637                        buf.write_leading("OFFSET 0 ROWS");
638                    }
639                    buf.write_leading("FETCH NEXT");
640                    buf.write_str(" ");
641                    buf.write_str(lim);
642                    buf.write_str(" ROWS ONLY");
643                }
644            }
645            Flavor::Informix | Flavor::Doris => {
646                // Future: handle Informix/Doris specific quirks if needed
647                if let Some(lim) = &self.limit_var {
648                    buf.write_leading("LIMIT");
649                    buf.write_str(" ");
650                    buf.write_str(lim);
651                    if let Some(off) = &self.offset_var {
652                        buf.write_leading("OFFSET");
653                        buf.write_str(" ");
654                        buf.write_str(off);
655                    }
656                }
657            }
658        }
659
660        if self.limit_var.is_some() {
661            write_injection(&mut buf, &self.injection, SELECT_MARKER_AFTER_LIMIT);
662        }
663
664        if let Some(what) = self.for_what {
665            buf.write_leading("FOR");
666            buf.write_str(" ");
667            buf.write_str(what);
668            write_injection(&mut buf, &self.injection, SELECT_MARKER_AFTER_FOR);
669        }
670
671        self.args
672            .borrow()
673            .compile_with_flavor(&buf.into_string(), flavor, initial_arg)
674    }
675
676    fn flavor(&self) -> Flavor {
677        self.flavor()
678    }
679}
680
681fn write_injection(buf: &mut StringBuilder, inj: &Injection, marker: InjectionMarker) {
682    let sqls = inj.at(marker);
683    if sqls.is_empty() {
684        return;
685    }
686    buf.write_leading("");
687    buf.write_str(&sqls.join(" "));
688}