Skip to main content

chain_builder/
builder.rs

1//! Main ChainBuilder implementation for building SQL queries
2
3use crate::query::QueryBuilder;
4use crate::types::{Client, Common, Method, Select};
5use serde_json::Value;
6
7/// Main query builder for constructing SQL queries
8///
9/// This is the primary interface for building SQL queries with a fluent API.
10/// It supports all major SQL operations including SELECT, INSERT, UPDATE, DELETE,
11/// as well as complex features like JOINs, CTEs, and subqueries.
12#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
13pub struct ChainBuilder {
14    /// Database client type
15    client: Client,
16    /// Database name
17    pub(crate) db: Option<String>,
18    /// Table name
19    pub(crate) table: Option<String>,
20    /// Raw table expression with optional bind parameters
21    pub(crate) table_raw: Option<(String, Option<Vec<Value>>)>,
22    /// Table alias
23    pub(crate) as_name: Option<String>,
24    /// SELECT clauses
25    pub(crate) select: Vec<Select>,
26    /// Query builder for WHERE clauses and other parts
27    pub(crate) query: QueryBuilder,
28    /// SQL operation method
29    pub(crate) method: Method,
30    /// Data for INSERT/UPDATE operations
31    pub(crate) insert_update: Value,
32    /// Generated SQL string (cached)
33    pub(crate) sql_str: String,
34    /// Whether to use DISTINCT
35    pub(crate) is_distinct: bool,
36}
37
38impl ChainBuilder {
39    /// Create a new ChainBuilder with the specified client
40    pub fn new(client: Client) -> ChainBuilder {
41        let query = QueryBuilder::new(client.clone());
42        ChainBuilder {
43            client,
44            table: None,
45            table_raw: None,
46            select: Vec::new(),
47            as_name: None,
48            db: None,
49            query,
50            method: Method::Select,
51            insert_update: Value::Null,
52            sql_str: String::new(),
53            is_distinct: false,
54        }
55    }
56
57    /// Create a new ChainBuilder for MySQL
58    #[cfg(feature = "mysql")]
59    pub fn new_mysql() -> ChainBuilder {
60        ChainBuilder::new(Client::Mysql)
61    }
62
63    #[cfg(feature = "sqlite")]
64    pub fn new_sqlite() -> ChainBuilder {
65        ChainBuilder::new(Client::Sqlite)
66    }
67
68    /// Set the database name
69    pub fn db(&mut self, db: &str) -> &mut ChainBuilder {
70        self.db = Some(db.to_string());
71        self
72    }
73
74    /// Set the table name
75    pub fn table(&mut self, table: &str) -> &mut ChainBuilder {
76        self.table = Some(table.to_string());
77        self
78    }
79
80    /// Set a raw table expression
81    pub fn table_raw(&mut self, table: &str, val: Option<Vec<Value>>) -> &mut ChainBuilder {
82        self.table_raw = Some((table.to_string(), val));
83        self
84    }
85
86    /// Enable DISTINCT
87    pub fn distinct(&mut self) -> &mut ChainBuilder {
88        self.is_distinct = true;
89        self
90    }
91
92    /// Add a SELECT clause
93    pub fn select(&mut self, select: Select) -> &mut ChainBuilder {
94        self.method = Method::Select;
95        self.select.push(select);
96        self
97    }
98
99    /// Add a raw SELECT expression
100    pub fn select_raw(&mut self, sql: &str, binds: Option<Vec<Value>>) -> &mut ChainBuilder {
101        self.method = Method::Select;
102        self.select.push(Select::Raw(sql.to_string(), binds));
103        self
104    }
105
106    /// Add DISTINCT SELECT
107    pub fn select_distinct(&mut self, columns: Vec<String>) -> &mut ChainBuilder {
108        self.method = Method::Select;
109        self.is_distinct = true;
110        self.select.push(Select::Columns(columns));
111        self
112    }
113
114    /// Add COUNT aggregate function
115    pub fn select_count(&mut self, column: &str) -> &mut ChainBuilder {
116        self.method = Method::Select;
117        let column = crate::dialect::escape_identifier(column, &self.client);
118        self.select.push(Select::Raw(format!("COUNT({})", column), None));
119        self
120    }
121
122    /// Add SUM aggregate function
123    pub fn select_sum(&mut self, column: &str) -> &mut ChainBuilder {
124        self.method = Method::Select;
125        let column = crate::dialect::escape_identifier(column, &self.client);
126        self.select.push(Select::Raw(format!("SUM({})", column), None));
127        self
128    }
129
130    /// Add AVG aggregate function
131    pub fn select_avg(&mut self, column: &str) -> &mut ChainBuilder {
132        self.method = Method::Select;
133        let column = crate::dialect::escape_identifier(column, &self.client);
134        self.select.push(Select::Raw(format!("AVG({})", column), None));
135        self
136    }
137
138    /// Add MAX aggregate function
139    pub fn select_max(&mut self, column: &str) -> &mut ChainBuilder {
140        self.method = Method::Select;
141        let column = crate::dialect::escape_identifier(column, &self.client);
142        self.select.push(Select::Raw(format!("MAX({})", column), None));
143        self
144    }
145
146    /// Add MIN aggregate function
147    pub fn select_min(&mut self, column: &str) -> &mut ChainBuilder {
148        self.method = Method::Select;
149        let column = crate::dialect::escape_identifier(column, &self.client);
150        self.select.push(Select::Raw(format!("MIN({})", column), None));
151        self
152    }
153
154    /// Add SELECT with alias
155    pub fn select_alias(&mut self, column: &str, alias: &str) -> &mut ChainBuilder {
156        self.method = Method::Select;
157        let column = crate::dialect::escape_identifier(column, &self.client);
158        let alias = crate::dialect::escape_identifier(alias, &self.client);
159        self.select
160            .push(Select::Raw(format!("{} AS {}", column, alias), None));
161        self
162    }
163
164    /// Set INSERT data
165    pub fn insert(&mut self, data: Value) -> &mut ChainBuilder {
166        self.method = Method::Insert;
167        self.insert_update = data;
168        self
169    }
170
171    /// Set INSERT multiple rows data
172    pub fn insert_many(&mut self, data: Vec<Value>) -> &mut ChainBuilder {
173        self.method = Method::InsertMany;
174        self.insert_update = Value::Array(data);
175        self
176    }
177
178    /// Set UPDATE data
179    pub fn update(&mut self, data: Value) -> &mut ChainBuilder {
180        self.method = Method::Update;
181        self.insert_update = data;
182        self
183    }
184
185    /// Add INSERT IGNORE (MySQL)
186    pub fn insert_ignore(&mut self, data: Value) -> &mut ChainBuilder {
187        self.method = Method::Insert;
188        self.insert_update = data;
189        // Note: This will need special handling in the SQL compiler
190        self
191    }
192
193    /// Add UPSERT (INSERT ... ON DUPLICATE KEY UPDATE)
194    pub fn insert_or_update(&mut self, data: Value, _update_data: Value) -> &mut ChainBuilder {
195        self.method = Method::Insert;
196        self.insert_update = data;
197        // Note: This will need special handling in the SQL compiler
198        self
199    }
200
201    /// Add raw UPDATE statement
202    pub fn update_raw(&mut self, _sql: &str, _binds: Option<Vec<Value>>) -> &mut ChainBuilder {
203        self.method = Method::Update;
204        // Note: This will need special handling in the SQL compiler
205        self
206    }
207
208    /// Increment a column value
209    pub fn increment(&mut self, column: &str, amount: i64) -> &mut ChainBuilder {
210        self.method = Method::Update;
211        let update_data = serde_json::json!({
212            column: format!("{} + {}", column, amount)
213        });
214        self.insert_update = update_data;
215        self
216    }
217
218    /// Decrement a column value
219    pub fn decrement(&mut self, column: &str, amount: i64) -> &mut ChainBuilder {
220        self.method = Method::Update;
221        let update_data = serde_json::json!({
222            column: format!("{} - {}", column, amount)
223        });
224        self.insert_update = update_data;
225        self
226    }
227
228    /// Set DELETE operation
229    pub fn delete(&mut self) -> &mut ChainBuilder {
230        self.method = Method::Delete;
231        self
232    }
233
234    /// Set table alias
235    pub fn as_name(&mut self, name: &str) -> &mut ChainBuilder {
236        self.as_name = Some(name.to_string());
237        self
238    }
239
240    /// Add a WITH clause (CTE)
241    pub fn with(&mut self, alias: &str, chain_builder: ChainBuilder) -> &mut ChainBuilder {
242        self.query
243            .query_common
244            .push(Common::With(alias.to_string(), false, chain_builder));
245        self
246    }
247
248    /// Add a recursive WITH clause (CTE)
249    pub fn with_recursive(
250        &mut self,
251        alias: &str,
252        chain_builder: ChainBuilder,
253    ) -> &mut ChainBuilder {
254        self.query
255            .query_common
256            .push(Common::With(alias.to_string(), true, chain_builder));
257        self
258    }
259
260    /// Add a UNION clause
261    pub fn union(&mut self, chain_builder: ChainBuilder) -> &mut ChainBuilder {
262        self.query
263            .query_common
264            .push(Common::Union(false, chain_builder));
265        self
266    }
267
268    /// Add a UNION ALL clause
269    pub fn union_all(&mut self, chain_builder: ChainBuilder) -> &mut ChainBuilder {
270        self.query
271            .query_common
272            .push(Common::Union(true, chain_builder));
273        self
274    }
275
276    /// Configure query parts (WHERE, JOIN, etc.)
277    pub fn query(&mut self, query: impl FnOnce(&mut QueryBuilder)) {
278        query(&mut self.query);
279    }
280
281    /// Add raw SQL
282    pub fn add_raw(&mut self, sql: &str, val: Option<Vec<Value>>) {
283        self.query.raw.push((sql.to_string(), val));
284    }
285
286    /// Generate SQL string and bind parameters
287    pub fn to_sql(&mut self) -> (String, Vec<Value>) {
288        match self.client {
289            #[cfg(feature = "mysql")]
290            Client::Mysql => {
291                let rs = crate::mysql::merge_to_sql(crate::mysql::to_sql(self));
292                self.sql_str = rs.0.clone();
293                (self.sql_str.clone(), rs.1)
294            }
295            #[cfg(feature = "sqlite")]
296            Client::Sqlite => {
297                let rs = crate::sqlite::merge_to_sql(crate::sqlite::to_sql(self));
298                self.sql_str = rs.0.clone();
299                (self.sql_str.clone(), rs.1)
300            }
301            #[cfg(feature = "postgres")]
302            Client::Postgres => {
303                panic!("PostgreSQL support not yet implemented");
304            }
305            _ => {
306                panic!("Unsupported database client");
307            }
308        }
309    }
310}