1use std::fmt::Write;
2
3use crate::{AdapterDialect, BinaryOperator, DinocoValue, Expression};
4
5pub fn push_joined<I, T, F>(buf: &mut String, iter: I, sep: &str, mut f: F)
6where
7 I: IntoIterator<Item = T>,
8 F: FnMut(&mut String, T),
9{
10 let mut first = true;
11
12 for item in iter {
13 if !first {
14 buf.push_str(sep);
15 }
16
17 f(buf, item);
18
19 first = false;
20 }
21}
22
23pub fn append_limit_skip<D: AdapterDialect + ?Sized>(
24 dialect: &D,
25 sql: &mut String,
26 params: &mut Vec<DinocoValue>,
27 limit: Option<usize>,
28 skip: Option<usize>,
29) {
30 match (limit, skip) {
31 (Some(limit), Some(skip)) => {
32 params.push(DinocoValue::Integer(limit as i64));
33 let _ = write!(sql, " LIMIT {}", dialect.bind_param(params.len()));
34
35 params.push(DinocoValue::Integer(skip as i64));
36 let _ = write!(sql, " OFFSET {}", dialect.bind_param(params.len()));
37 }
38 (Some(limit), None) => {
39 params.push(DinocoValue::Integer(limit as i64));
40 let _ = write!(sql, " LIMIT {}", dialect.bind_param(params.len()));
41 }
42 (None, Some(skip)) => {
43 let _ = write!(sql, " LIMIT {}", dialect.offset_without_limit());
44 params.push(DinocoValue::Integer(skip as i64));
45
46 let _ = write!(sql, " OFFSET {}", dialect.bind_param(params.len()));
47 }
48 (None, None) => {}
49 }
50}
51
52pub fn render_condition_group_into<D: AdapterDialect + ?Sized>(
53 dialect: &D,
54 conditions: &[Expression],
55 params: &mut Vec<DinocoValue>,
56 joiner: &str,
57 buf: &mut String,
58) {
59 push_joined(buf, conditions, joiner, |b, condition| {
60 render_expression_into(dialect, condition, params, b);
61 });
62}
63
64pub fn render_expression_into<D: AdapterDialect + ?Sized>(
65 dialect: &D,
66 expression: &Expression,
67 params: &mut Vec<DinocoValue>,
68 buf: &mut String,
69) {
70 match expression {
71 Expression::Column(name) => render_query_identifier_into(dialect, name, buf),
72 Expression::Value(value) => {
73 params.push(value.clone());
74
75 buf.push_str(&dialect.bind_value(params.len(), value));
76 }
77 Expression::Raw(value) => buf.push_str(value),
78 Expression::IsNull(inner) => {
79 buf.push('(');
80
81 render_expression_into(dialect, inner, params, buf);
82
83 buf.push_str(" IS NULL)");
84 }
85 Expression::IsNotNull(inner) => {
86 buf.push('(');
87
88 render_expression_into(dialect, inner, params, buf);
89
90 buf.push_str(" IS NOT NULL)");
91 }
92 Expression::In { expr, values } => {
93 if values.is_empty() {
94 buf.push_str("(1 = 0)");
95
96 return;
97 }
98
99 params.reserve(values.len());
100
101 buf.push('(');
102
103 render_expression_into(dialect, expr, params, buf);
104
105 buf.push_str(" IN (");
106
107 push_joined(buf, values, ", ", |b, value| {
108 params.push(value.clone());
109
110 b.push_str(&dialect.bind_value(params.len(), value));
111 });
112
113 buf.push_str("))");
114 }
115 Expression::NotIn { expr, values } => {
116 if values.is_empty() {
117 buf.push_str("(1 = 1)");
118
119 return;
120 }
121
122 params.reserve(values.len());
123
124 buf.push('(');
125
126 render_expression_into(dialect, expr, params, buf);
127
128 buf.push_str(" NOT IN (");
129
130 push_joined(buf, values, ", ", |b, value| {
131 params.push(value.clone());
132
133 b.push_str(&dialect.bind_value(params.len(), value));
134 });
135
136 buf.push_str("))");
137 }
138 Expression::And(expressions) => {
139 if expressions.is_empty() {
140 buf.push_str("(1 = 1)");
141
142 return;
143 }
144
145 buf.push('(');
146
147 render_condition_group_into(dialect, expressions, params, " AND ", buf);
148
149 buf.push(')');
150 }
151 Expression::Or(expressions) => {
152 if expressions.is_empty() {
153 buf.push_str("(1 = 0)");
154
155 return;
156 }
157
158 buf.push('(');
159
160 render_condition_group_into(dialect, expressions, params, " OR ", buf);
161
162 buf.push(')');
163 }
164 Expression::BinaryOp { left, op, right } => {
165 buf.push('(');
166
167 render_expression_into(dialect, left, params, buf);
168
169 let op_str = match op {
170 BinaryOperator::Eq => " = ",
171 BinaryOperator::Neq => " <> ",
172 BinaryOperator::Gt => " > ",
173 BinaryOperator::Lt => " < ",
174 BinaryOperator::Gte => " >= ",
175 BinaryOperator::Lte => " <= ",
176 BinaryOperator::Like => " LIKE ",
177 };
178
179 buf.push_str(op_str);
180
181 render_expression_into(dialect, right, params, buf);
182
183 buf.push(')');
184 }
185 }
186}
187
188pub fn render_query_identifier_into<D: AdapterDialect + ?Sized>(dialect: &D, value: &str, buf: &mut String) {
189 if value == "*" || value.contains(' ') || value.contains('(') || value.contains(')') || value.contains(',') {
190 buf.push_str(value);
191
192 return;
193 }
194
195 let mut first = true;
196
197 for part in value.split('.') {
198 if !first {
199 buf.push('.');
200 }
201
202 if part == "*" {
203 buf.push('*');
204 } else {
205 buf.push_str(&dialect.identifier(part));
206 }
207
208 first = false;
209 }
210}