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