1#![warn(missing_docs)]
2
3use crate::{filter::Filtered, schema::Value};
73
74pub mod database;
76
77pub mod filter;
79
80pub mod operations;
82
83pub mod row;
85
86pub mod schema;
88
89pub mod table;
91
92mod tests;
93
94#[derive(PartialEq, Debug)]
95pub(crate) enum StartingSql {
96 Select,
97 Insert,
98 Delete,
99 Update,
100}
101
102pub(crate) fn get_starting_sql(starting_sql: StartingSql, table_name: &str) -> String {
103 match starting_sql {
104 StartingSql::Select => "SELECT ".to_string(),
105 StartingSql::Insert => format!("INSERT INTO `{}` (", table_name),
106 StartingSql::Delete => format!("DELETE FROM `{}` ", table_name),
107 StartingSql::Update => format!("UPDATE `{}` SET ", table_name),
108 }
109}
110
111#[cfg(not(feature = "mysql"))]
112pub(crate) fn returning_sql(mut sql: String, returning: &Vec<&'static str>) -> String {
113 if returning.is_empty() {
114 return sql;
115 }
116
117 sql.push_str(" RETURNING ");
118 for (i, col) in returning.iter().enumerate() {
119 if i > 0 {
120 sql.push_str(", ");
121 }
122 sql.push_str(col);
123 }
124 sql.push_str(";");
125 sql
126}
127
128#[cfg(feature = "mysql")]
129pub(crate) fn returning_sql(mut sql: String, returning: &Vec<&'static str>) -> String {
130 if returning.is_empty() {
131 return sql;
132 }
133 sql.push_str(&returning.join(", "));
134
135 sql
136}
137
138pub(crate) fn build_filter_expr(filter: &dyn Filtered, params: &mut Vec<Value>) -> String {
139 if filter.is_or_filter() || filter.is_and_filter() {
140 let op = if filter.is_or_filter() { "OR" } else { "AND" };
141 let Some(f1) = filter.filter1() else {
142 eprintln!("Warning: Composite filter missing filter1, using tautology");
143 return "1=1".to_string();
144 };
145 let Some(f2) = filter.filter2() else {
146 eprintln!("Warning: Composite filter missing filter2, using tautology");
147 return "1=1".to_string();
148 };
149 let left = build_filter_expr(f1, params);
150 let right = build_filter_expr(f2, params);
151 return format!("({} {} {})", left, op, right);
152 }
153
154 if filter.is_not().unwrap_or(false) {
155 let Some(f) = filter.filter1() else {
156 eprintln!("Warning: Not filter missing filter1, using tautology");
157 return "1=1".to_string();
158 };
159 return format!("NOT ({})", build_filter_expr(f, params));
160 }
161
162 let Some(col1) = filter.column_one() else {
163 eprintln!("Warning: Simple filter missing column_one, using tautology");
164 return "1=1".to_string();
165 };
166 if let Some(in_array) = filter.is_in_array() {
168 let values = filter.array_values().unwrap_or(&[]);
169 if values.is_empty() {
170 return if in_array {
171 "1=0".to_string()
172 } else {
173 "1=1".to_string()
174 };
175 }
176 let mut placeholders: Vec<&'static str> = Vec::with_capacity(values.len());
177 for v in values.iter().cloned() {
178 params.push(v);
179 placeholders.push("?");
180 }
181 let op = if in_array { "IN" } else { "NOT IN" };
182 return format!("{}.{} {} ({})", col1.0, col1.1, op, placeholders.join(", "));
183 }
184 if let Some(value) = filter.value() {
185 match value {
186 Value::Null => {
187 let op = filter.filter_type();
189 let null_sql = match op {
190 crate::filter::FilterType::Eq => "IS NULL",
191 crate::filter::FilterType::Neq => "IS NOT NULL",
192 _ => {
193 return "1=0".to_string();
195 }
196 };
197 format!("{}.{} {}", col1.0, col1.1, null_sql)
198 }
199 Value::Between(min, max) => {
200 params.push((**min).clone());
201 params.push((**max).clone());
202 format!("{}.{} BETWEEN ? AND ?", col1.0, col1.1)
203 }
204 _ => {
205 params.push(value.clone());
206 format!("{}.{} {} ?", col1.0, col1.1, filter.filter_type().to_sql())
207 }
208 }
209 } else if let Some(col2) = filter.column_two() {
210 format!(
211 "{}.{} {} {}.{}",
212 col1.0,
213 col1.1,
214 filter.filter_type().to_sql(),
215 col2.0,
216 col2.1
217 )
218 } else {
219 return "1=1".to_string();
220 }
221}