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        self.select
118            .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        self.select
126            .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        self.select
134            .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        self.select
142            .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        self.select
150            .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        self.select
158            .push(Select::Raw(format!("{} AS {}", column, alias), None));
159        self
160    }
161
162    /// Set INSERT data
163    pub fn insert(&mut self, data: Value) -> &mut ChainBuilder {
164        self.method = Method::Insert;
165        self.insert_update = data;
166        self
167    }
168
169    /// Set INSERT multiple rows data
170    pub fn insert_many(&mut self, data: Vec<Value>) -> &mut ChainBuilder {
171        self.method = Method::InsertMany;
172        self.insert_update = Value::Array(data);
173        self
174    }
175
176    /// Set UPDATE data
177    pub fn update(&mut self, data: Value) -> &mut ChainBuilder {
178        self.method = Method::Update;
179        self.insert_update = data;
180        self
181    }
182
183    /// Add INSERT IGNORE (MySQL)
184    pub fn insert_ignore(&mut self, data: Value) -> &mut ChainBuilder {
185        self.method = Method::Insert;
186        self.insert_update = data;
187        // Note: This will need special handling in the SQL compiler
188        self
189    }
190
191    /// Add UPSERT (INSERT ... ON DUPLICATE KEY UPDATE)
192    pub fn insert_or_update(&mut self, data: Value, _update_data: Value) -> &mut ChainBuilder {
193        self.method = Method::Insert;
194        self.insert_update = data;
195        // Note: This will need special handling in the SQL compiler
196        self
197    }
198
199    /// Add raw UPDATE statement
200    pub fn update_raw(&mut self, _sql: &str, _binds: Option<Vec<Value>>) -> &mut ChainBuilder {
201        self.method = Method::Update;
202        // Note: This will need special handling in the SQL compiler
203        self
204    }
205
206    /// Increment a column value
207    pub fn increment(&mut self, column: &str, amount: i64) -> &mut ChainBuilder {
208        self.method = Method::Update;
209        let update_data = serde_json::json!({
210            column: format!("{} + {}", column, amount)
211        });
212        self.insert_update = update_data;
213        self
214    }
215
216    /// Decrement a column value
217    pub fn decrement(&mut self, column: &str, amount: i64) -> &mut ChainBuilder {
218        self.method = Method::Update;
219        let update_data = serde_json::json!({
220            column: format!("{} - {}", column, amount)
221        });
222        self.insert_update = update_data;
223        self
224    }
225
226    /// Set DELETE operation
227    pub fn delete(&mut self) -> &mut ChainBuilder {
228        self.method = Method::Delete;
229        self
230    }
231
232    /// Set table alias
233    pub fn as_name(&mut self, name: &str) -> &mut ChainBuilder {
234        self.as_name = Some(name.to_string());
235        self
236    }
237
238    /// Add a WITH clause (CTE)
239    pub fn with(&mut self, alias: &str, chain_builder: ChainBuilder) -> &mut ChainBuilder {
240        self.query
241            .query_common
242            .push(Common::With(alias.to_string(), false, chain_builder));
243        self
244    }
245
246    /// Add a recursive WITH clause (CTE)
247    pub fn with_recursive(
248        &mut self,
249        alias: &str,
250        chain_builder: ChainBuilder,
251    ) -> &mut ChainBuilder {
252        self.query
253            .query_common
254            .push(Common::With(alias.to_string(), true, chain_builder));
255        self
256    }
257
258    /// Add a UNION clause
259    pub fn union(&mut self, chain_builder: ChainBuilder) -> &mut ChainBuilder {
260        self.query
261            .query_common
262            .push(Common::Union(false, chain_builder));
263        self
264    }
265
266    /// Add a UNION ALL clause
267    pub fn union_all(&mut self, chain_builder: ChainBuilder) -> &mut ChainBuilder {
268        self.query
269            .query_common
270            .push(Common::Union(true, chain_builder));
271        self
272    }
273
274    /// Configure query parts (WHERE, JOIN, etc.)
275    pub fn query(&mut self, query: impl FnOnce(&mut QueryBuilder)) {
276        query(&mut self.query);
277    }
278
279    /// Add raw SQL
280    pub fn add_raw(&mut self, sql: &str, val: Option<Vec<Value>>) {
281        self.query.raw.push((sql.to_string(), val));
282    }
283
284    /// Generate SQL string and bind parameters
285    pub fn to_sql(&mut self) -> (String, Vec<Value>) {
286        match self.client {
287            #[cfg(feature = "mysql")]
288            Client::Mysql => {
289                let rs = crate::mysql::merge_to_sql(crate::mysql::to_sql(self));
290                self.sql_str = rs.0.clone();
291                (self.sql_str.clone(), rs.1)
292            }
293            #[cfg(feature = "sqlite")]
294            Client::Sqlite => {
295                let rs = crate::sqlite::merge_to_sql(crate::sqlite::to_sql(self));
296                self.sql_str = rs.0.clone();
297                (self.sql_str.clone(), rs.1)
298            }
299            #[cfg(feature = "postgres")]
300            Client::Postgres => {
301                panic!("PostgreSQL support not yet implemented");
302            }
303            _ => {
304                panic!("Unsupported database client");
305            }
306        }
307    }
308}