1use crate::args::Args;
4use crate::flavor::Flavor;
5use crate::injection::{Injection, InjectionMarker};
6use crate::macros::{IntoStrings, collect_into_strings};
7use crate::modifiers::{Arg, Builder};
8use crate::string_builder::StringBuilder;
9use std::cell::RefCell;
10use std::rc::Rc;
11
12const CTE_QUERY_MARKER_INIT: InjectionMarker = 0;
13const CTE_QUERY_MARKER_AFTER_TABLE: InjectionMarker = 1;
14const CTE_QUERY_MARKER_AFTER_AS: InjectionMarker = 2;
15
16#[derive(Default)]
17pub struct CTEQueryBuilder {
18 name: Option<String>,
19 cols: Vec<String>,
20 builder_var: Option<String>,
21 #[allow(clippy::type_complexity)]
22 builder: Option<Box<dyn Builder>>,
23 auto_add_to_table_list: bool,
24
25 args: Rc<RefCell<Args>>,
26 injection: Injection,
27 marker: InjectionMarker,
28}
29
30impl std::fmt::Debug for CTEQueryBuilder {
32 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
33 f.debug_struct("CTEQueryBuilder")
34 .field("name", &self.name)
35 .field("cols", &self.cols)
36 .field("builder_var", &self.builder_var)
37 .field("auto_add_to_table_list", &self.auto_add_to_table_list)
38 .finish()
39 }
40}
41
42impl Clone for CTEQueryBuilder {
43 fn clone(&self) -> Self {
44 self.clone_builder()
45 }
46}
47
48impl CTEQueryBuilder {
49 pub fn new() -> Self {
50 Self {
51 name: None,
52 cols: Vec::new(),
53 builder_var: None,
54 builder: None,
55 auto_add_to_table_list: false,
56 args: Rc::new(RefCell::new(Args::default())),
57 injection: Injection::new(),
58 marker: CTE_QUERY_MARKER_INIT,
59 }
60 }
61
62 pub fn set_flavor(&mut self, flavor: Flavor) -> Flavor {
63 let mut a = self.args.borrow_mut();
64 let old = a.flavor;
65 a.flavor = flavor;
66 old
67 }
68
69 pub fn flavor(&self) -> Flavor {
70 self.args.borrow().flavor
71 }
72
73 pub fn clone_builder(&self) -> Self {
74 let cloned = Self {
75 name: self.name.clone(),
76 cols: self.cols.clone(),
77 builder_var: self.builder_var.clone(),
78 builder: self
79 .builder
80 .as_ref()
81 .map(|b| dyn_clone::clone_box(b.as_ref())),
82 auto_add_to_table_list: self.auto_add_to_table_list,
83 args: Rc::new(RefCell::new(self.args.borrow().clone())),
84 injection: self.injection.clone(),
85 marker: self.marker,
86 };
87
88 if let (Some(ph), Some(b)) = (&self.builder_var, &self.builder) {
89 cloned
90 .args
91 .borrow_mut()
92 .replace(ph, Arg::Builder(dyn_clone::clone_box(b.as_ref())));
93 }
94
95 cloned
96 }
97
98 fn var(&self, v: impl Into<Arg>) -> String {
99 self.args.borrow_mut().add(v)
100 }
101
102 pub fn table<T>(&mut self, name: impl Into<String>, cols: T) -> &mut Self
103 where
104 T: IntoStrings,
105 {
106 self.name = Some(name.into());
107 self.cols = collect_into_strings(cols);
108 self.marker = CTE_QUERY_MARKER_AFTER_TABLE;
109 self
110 }
111
112 pub fn as_(&mut self, builder: impl Builder + 'static) -> &mut Self {
113 let b: Box<dyn Builder> = Box::new(builder);
114 let ph = self.var(Arg::Builder(dyn_clone::clone_box(b.as_ref())));
115 self.builder = Some(b);
116 self.builder_var = Some(ph);
117 self.marker = CTE_QUERY_MARKER_AFTER_AS;
118 self
119 }
120
121 pub fn add_to_table_list(&mut self) -> &mut Self {
122 self.auto_add_to_table_list = true;
123 self
124 }
125
126 pub fn should_add_to_table_list(&self) -> bool {
127 self.auto_add_to_table_list
128 }
129
130 pub fn table_name(&self) -> Option<&str> {
131 self.name.as_deref()
132 }
133
134 pub fn sql(&mut self, sql: impl Into<String>) -> &mut Self {
135 self.injection.sql(self.marker, sql);
136 self
137 }
138}
139
140impl Builder for CTEQueryBuilder {
141 fn build_with_flavor(&self, flavor: Flavor, initial_arg: &[Arg]) -> (String, Vec<Arg>) {
142 let mut buf = StringBuilder::new();
143 write_injection(&mut buf, &self.injection, CTE_QUERY_MARKER_INIT);
144
145 if let Some(name) = &self.name {
146 buf.write_leading(name);
147 if !self.cols.is_empty() {
148 buf.write_str(" (");
149 buf.write_str(&self.cols.join(", "));
150 buf.write_str(")");
151 }
152 write_injection(&mut buf, &self.injection, CTE_QUERY_MARKER_AFTER_TABLE);
153 }
154
155 if let Some(ph) = &self.builder_var {
156 buf.write_leading("AS (");
157 buf.write_str(ph);
158 buf.write_str(")");
159 write_injection(&mut buf, &self.injection, CTE_QUERY_MARKER_AFTER_AS);
160 }
161
162 self.args
163 .borrow()
164 .compile_with_flavor(&buf.into_string(), flavor, initial_arg)
165 }
166
167 fn flavor(&self) -> Flavor {
168 self.flavor()
169 }
170}
171
172fn write_injection(buf: &mut StringBuilder, inj: &Injection, marker: InjectionMarker) {
173 let sqls = inj.at(marker);
174 if sqls.is_empty() {
175 return;
176 }
177 buf.write_leading("");
178 buf.write_str(&sqls.join(" "));
179}