1use crate::dialect::Dialect;
8use crate::expr::{BinOp, CastTarget, ExprKind, SortDir, Value};
9
10pub(crate) trait Sink {
15 fn text(&mut self, s: &str);
16 fn ch(&mut self, c: char);
17 fn bind(&mut self, value: &Value);
18}
19
20#[doc(hidden)]
22pub struct QuerySink<D> {
23 sql: String,
24 params: Vec<Value>,
25 _dialect: std::marker::PhantomData<fn() -> D>,
26}
27
28impl<D: Dialect> QuerySink<D> {
29 pub(crate) fn new() -> Self {
30 QuerySink {
31 sql: String::new(),
32 params: Vec::new(),
33 _dialect: std::marker::PhantomData,
34 }
35 }
36
37 pub(crate) fn finish(self) -> (String, Vec<Value>) {
38 (self.sql, self.params)
39 }
40}
41
42impl<D: Dialect> Sink for QuerySink<D> {
43 fn text(&mut self, s: &str) {
44 self.sql.push_str(s);
45 }
46 fn ch(&mut self, c: char) {
47 self.sql.push(c);
48 }
49 fn bind(&mut self, value: &Value) {
50 self.params.push(value.clone());
51 D::write_placeholder(self.params.len(), &mut self.sql);
52 }
53}
54
55pub(crate) struct FragmentSink(Fragment);
58
59impl FragmentSink {
60 pub(crate) fn new() -> Self {
61 FragmentSink(Fragment::empty())
62 }
63
64 pub(crate) fn finish(self) -> Fragment {
65 self.0
66 }
67}
68
69impl Sink for FragmentSink {
70 fn text(&mut self, s: &str) {
71 self.0.tail().push_str(s);
72 }
73 fn ch(&mut self, c: char) {
74 self.0.tail().push(c);
75 }
76 fn bind(&mut self, value: &Value) {
77 self.0.rest.push((value.clone(), String::new()));
78 }
79}
80
81pub(crate) fn render_expr<D: Dialect>(expr: &ExprKind, sink: &mut dyn Sink) {
82 match expr {
83 ExprKind::Column { table, name } => {
84 render_ident::<D>(sink, table);
85 sink.ch('.');
86 render_ident::<D>(sink, name);
87 }
88 ExprKind::Value(v) => sink.bind(v),
89 ExprKind::BinOp { op, lhs, rhs } => {
90 sink.ch('(');
91 render_expr::<D>(lhs, sink);
92 sink.text(match op {
93 BinOp::Eq => " = ",
94 BinOp::Ne => " <> ",
95 BinOp::Lt => " < ",
96 BinOp::Lte => " <= ",
97 BinOp::Gt => " > ",
98 BinOp::Gte => " >= ",
99 BinOp::Like => " LIKE ",
100 });
101 render_expr::<D>(rhs, sink);
102 sink.ch(')');
103 }
104 ExprKind::And(lhs, rhs) => render_bool_pair::<D>(lhs, "AND", rhs, sink),
105 ExprKind::Or(lhs, rhs) => render_bool_pair::<D>(lhs, "OR", rhs, sink),
106 ExprKind::Not(inner) => {
107 sink.text("(NOT ");
108 render_expr::<D>(inner, sink);
109 sink.ch(')');
110 }
111 ExprKind::Cast { expr, target } => {
112 sink.text("CAST(");
113 render_expr::<D>(expr, sink);
114 sink.text(" AS ");
115 sink.text(match target {
116 CastTarget::BigInt => D::CAST_BIGINT,
117 CastTarget::Double => D::CAST_DOUBLE,
118 });
119 sink.ch(')');
120 }
121 ExprKind::Func { name, arg } => {
122 sink.text(name);
123 sink.ch('(');
124 match arg {
125 Some(arg) => render_expr::<D>(arg, sink),
126 None => sink.ch('*'),
127 }
128 sink.ch(')');
129 }
130 ExprKind::IsNull { expr, negated } => {
131 sink.ch('(');
132 render_expr::<D>(expr, sink);
133 sink.text(if *negated {
134 " IS NOT NULL)"
135 } else {
136 " IS NULL)"
137 });
138 }
139 ExprKind::Always(yes) => sink.text(if *yes { "TRUE" } else { "FALSE" }),
140 ExprKind::InList { expr, values } => {
141 sink.ch('(');
142 render_expr::<D>(expr, sink);
143 sink.text(" IN (");
144 for (i, v) in values.iter().enumerate() {
145 if i > 0 {
146 sink.text(", ");
147 }
148 render_expr::<D>(v, sink);
149 }
150 sink.text("))");
151 }
152 ExprKind::Exists {
153 body,
154 selection,
155 negated,
156 } => {
157 sink.text(if *negated {
158 "(NOT EXISTS ("
159 } else {
160 "(EXISTS ("
161 });
162 body.render_into::<D>(selection, sink);
163 sink.text("))");
164 }
165 ExprKind::Template { head, rest } => {
166 sink.ch('(');
169 sink.text(head);
170 for (arg, text) in rest {
171 render_expr::<D>(arg, sink);
172 sink.text(text);
173 }
174 sink.ch(')');
175 }
176 ExprKind::Window {
177 func,
178 partition_by,
179 order_by,
180 } => {
181 sink.text(func);
185 sink.text(" OVER (");
186 render_expr_list::<D>(sink, "PARTITION BY ", partition_by);
187 let keyword = if partition_by.is_empty() {
188 "ORDER BY "
189 } else {
190 " ORDER BY "
191 };
192 render_order_by::<D>(sink, keyword, order_by);
193 sink.ch(')');
194 }
195 }
196}
197
198#[doc(hidden)]
199#[derive(Debug, Clone)]
200pub struct SelectItem {
204 pub(crate) kind: ExprKind,
205 pub(crate) label: Option<&'static str>,
206}
207
208impl SelectItem {
209 pub(crate) fn bare(kind: ExprKind) -> Self {
210 SelectItem { kind, label: None }
211 }
212
213 pub(crate) fn labeled(kind: ExprKind, label: &'static str) -> Self {
214 SelectItem {
215 kind,
216 label: Some(label),
217 }
218 }
219}
220
221pub(crate) fn render_select_list<D: Dialect>(items: &[SelectItem], sink: &mut dyn Sink) {
224 for (i, item) in items.iter().enumerate() {
225 if i > 0 {
226 sink.text(", ");
227 }
228 render_expr::<D>(&item.kind, sink);
229 if let Some(label) = item.label {
230 sink.text(" AS ");
231 render_ident::<D>(sink, label);
232 }
233 }
234}
235
236#[derive(Debug, Clone)]
244pub(crate) struct Fragment {
245 head: String,
246 rest: Vec<(Value, String)>,
247}
248
249impl Fragment {
250 fn empty() -> Self {
251 Fragment {
252 head: String::new(),
253 rest: Vec::new(),
254 }
255 }
256
257 fn tail(&mut self) -> &mut String {
260 match self.rest.last_mut() {
261 Some((_, text)) => text,
262 None => &mut self.head,
263 }
264 }
265
266 pub(crate) fn splice_into(&self, sink: &mut dyn Sink) {
269 sink.text(&self.head);
270 for (value, text) in &self.rest {
271 sink.bind(value);
272 sink.text(text);
273 }
274 }
275}
276
277pub(crate) fn render_ident<D: Dialect>(sink: &mut dyn Sink, ident: &str) {
283 for (i, part) in ident.split('.').enumerate() {
284 if i > 0 {
285 sink.ch('.');
286 }
287 render_ident_part::<D>(sink, part);
288 }
289}
290
291fn render_ident_part<D: Dialect>(sink: &mut dyn Sink, ident: &str) {
292 sink.ch(D::IDENTIFIER_QUOTE);
293 for c in ident.chars() {
294 if c == D::IDENTIFIER_QUOTE {
298 sink.ch(c);
299 }
300 sink.ch(c);
301 }
302 sink.ch(D::IDENTIFIER_QUOTE);
303}
304
305fn render_bool_pair<D: Dialect>(lhs: &ExprKind, joiner: &str, rhs: &ExprKind, sink: &mut dyn Sink) {
306 sink.ch('(');
307 render_expr::<D>(lhs, sink);
308 sink.ch(' ');
309 sink.text(joiner);
310 sink.ch(' ');
311 render_expr::<D>(rhs, sink);
312 sink.ch(')');
313}
314
315pub(crate) fn dir_keyword(dir: SortDir) -> &'static str {
319 match dir {
320 SortDir::Asc => " ASC",
321 SortDir::Desc => " DESC",
322 }
323}
324
325pub(crate) fn render_count_wrapped<D: Dialect>(
329 sink: &mut QuerySink<D>,
330 body: impl FnOnce(&mut QuerySink<D>),
331) {
332 sink.text("SELECT count(*) FROM (");
333 body(sink);
334 sink.text(") AS ");
335 render_ident::<D>(sink, "qbrs_total");
336}
337
338pub(crate) fn render_expr_list<D: Dialect>(sink: &mut dyn Sink, keyword: &str, list: &[ExprKind]) {
341 if list.is_empty() {
342 return;
343 }
344 sink.text(keyword);
345 for (i, e) in list.iter().enumerate() {
346 if i > 0 {
347 sink.text(", ");
348 }
349 render_expr::<D>(e, sink);
350 }
351}
352
353pub(crate) fn render_order_by<D: Dialect>(
356 sink: &mut dyn Sink,
357 keyword: &str,
358 keys: &[(ExprKind, SortDir)],
359) {
360 if keys.is_empty() {
361 return;
362 }
363 sink.text(keyword);
364 for (i, (e, dir)) in keys.iter().enumerate() {
365 if i > 0 {
366 sink.text(", ");
367 }
368 render_expr::<D>(e, sink);
369 sink.text(dir_keyword(*dir));
370 }
371}
372
373pub(crate) fn render_and_list<D: Dialect>(sink: &mut dyn Sink, keyword: &str, list: &[ExprKind]) {
377 if list.is_empty() {
378 return;
379 }
380 sink.text(keyword);
381 for (i, e) in list.iter().enumerate() {
382 if i > 0 {
383 sink.text(" AND ");
384 }
385 render_expr::<D>(e, sink);
386 }
387}