elif_orm/query/
performance_optimized.rs

1//! Performance optimized query building
2//!
3//! This module implements optimized versions of query building operations
4//! to reduce allocations and improve performance for hot paths.
5
6use super::builder::QueryBuilder;
7use once_cell::sync::Lazy;
8use std::collections::HashMap;
9use std::sync::{Arc, RwLock};
10
11/// Cache for common SQL patterns to reduce string allocations
12#[allow(dead_code)]
13static QUERY_TEMPLATE_CACHE: Lazy<RwLock<HashMap<String, String>>> =
14    Lazy::new(|| RwLock::new(HashMap::new()));
15
16/// Cache for parameter placeholders to avoid repeated generation
17static PLACEHOLDER_CACHE: Lazy<RwLock<HashMap<usize, String>>> =
18    Lazy::new(|| RwLock::new(HashMap::new()));
19
20/// Performance-optimized SQL generation with caching
21impl<M> QueryBuilder<M> {
22    /// Generate parameter placeholders with caching for better performance
23    pub fn generate_placeholders_cached(count: usize) -> String {
24        // Check cache first
25        if let Ok(cache) = PLACEHOLDER_CACHE.read() {
26            if let Some(cached) = cache.get(&count) {
27                return cached.clone();
28            }
29        }
30
31        // Generate placeholders
32        let placeholders = (1..=count)
33            .map(|i| format!("${}", i))
34            .collect::<Vec<_>>()
35            .join(", ");
36
37        // Cache for future use
38        if let Ok(mut cache) = PLACEHOLDER_CACHE.write() {
39            cache.insert(count, placeholders.clone());
40        }
41
42        placeholders
43    }
44
45    /// Generate sequential parameter placeholders starting from a specific index
46    /// Used for proper parameter ordering in complex queries
47    pub fn generate_sequential_placeholders(start_index: usize, count: usize) -> String {
48        if count == 0 {
49            return String::new();
50        }
51
52        let placeholders = (start_index..start_index + count)
53            .map(|i| format!("${}", i))
54            .collect::<Vec<_>>()
55            .join(", ");
56
57        placeholders
58    }
59
60    /// Optimized SQL generation with pre-allocated capacity
61    pub fn to_sql_optimized(&self) -> String {
62        // Pre-calculate approximate SQL length to reduce allocations
63        let estimated_length = self.estimate_sql_length();
64        let mut sql = String::with_capacity(estimated_length);
65
66        match self.query_type {
67            super::types::QueryType::Select => {
68                self.build_select_sql_optimized(&mut sql);
69            }
70            _ => {
71                // Fallback to regular implementation for non-SELECT queries
72                return self.to_sql();
73            }
74        }
75
76        sql
77    }
78
79    /// Estimate SQL length to pre-allocate string capacity
80    fn estimate_sql_length(&self) -> usize {
81        let mut length = 100; // Base SQL overhead
82
83        // Estimate SELECT clause length
84        for field in &self.select_fields {
85            length += field.len() + 2; // field + ", "
86        }
87
88        // Estimate FROM clause length
89        for table in &self.from_tables {
90            length += table.len() + 10; // " FROM " + table
91        }
92
93        // Estimate WHERE clause length
94        for condition in &self.where_conditions {
95            length += condition.column.len() + 20; // column + operator + placeholder
96        }
97
98        // Estimate JOIN clause length
99        for join in &self.joins {
100            length += join.table.len() + 30; // JOIN type + table + ON condition
101        }
102
103        length
104    }
105
106    /// Build SELECT SQL with optimized string operations and correct parameter indexing
107    fn build_select_sql_optimized(&self, sql: &mut String) {
108        let mut param_counter = 1usize;
109        // SELECT clause
110        if self.distinct {
111            sql.push_str("SELECT DISTINCT ");
112        } else {
113            sql.push_str("SELECT ");
114        }
115
116        // Fields
117        if self.select_fields.is_empty() {
118            sql.push('*');
119        } else {
120            for (i, field) in self.select_fields.iter().enumerate() {
121                if i > 0 {
122                    sql.push_str(", ");
123                }
124                sql.push_str(field);
125            }
126        }
127
128        // FROM clause
129        if !self.from_tables.is_empty() {
130            sql.push_str(" FROM ");
131            for (i, table) in self.from_tables.iter().enumerate() {
132                if i > 0 {
133                    sql.push_str(", ");
134                }
135                sql.push_str(table);
136            }
137        }
138
139        // JOINs
140        for join in &self.joins {
141            sql.push(' ');
142            match join.join_type {
143                super::types::JoinType::Inner => sql.push_str("INNER JOIN"),
144                super::types::JoinType::Left => sql.push_str("LEFT JOIN"),
145                super::types::JoinType::Right => sql.push_str("RIGHT JOIN"),
146                super::types::JoinType::Full => sql.push_str("FULL JOIN"),
147            }
148            sql.push(' ');
149            sql.push_str(&join.table);
150            sql.push_str(" ON ");
151
152            // Handle on_conditions
153            for (i, (left_col, right_col)) in join.on_conditions.iter().enumerate() {
154                if i > 0 {
155                    sql.push_str(" AND ");
156                }
157                sql.push_str(left_col);
158                sql.push_str(" = ");
159                sql.push_str(right_col);
160            }
161        }
162
163        // WHERE clause
164        if !self.where_conditions.is_empty() {
165            sql.push_str(" WHERE ");
166            for (i, condition) in self.where_conditions.iter().enumerate() {
167                if i > 0 {
168                    sql.push_str(" AND ");
169                }
170
171                // Handle special cases
172                if condition.column == "RAW" {
173                    if let Some(ref value) = condition.value {
174                        if let serde_json::Value::String(raw_sql) = value {
175                            sql.push_str(raw_sql);
176                        }
177                    }
178                } else if condition.column == "EXISTS" || condition.column == "NOT EXISTS" {
179                    sql.push_str(&condition.column);
180                    sql.push(' ');
181                    if let Some(ref value) = condition.value {
182                        if let serde_json::Value::String(subquery) = value {
183                            sql.push_str(subquery);
184                        }
185                    }
186                } else {
187                    // Regular conditions
188                    sql.push_str(&condition.column);
189
190                    match condition.operator {
191                        super::types::QueryOperator::Equal => sql.push_str(" = "),
192                        super::types::QueryOperator::NotEqual => sql.push_str(" != "),
193                        super::types::QueryOperator::GreaterThan => sql.push_str(" > "),
194                        super::types::QueryOperator::LessThan => sql.push_str(" < "),
195                        super::types::QueryOperator::GreaterThanOrEqual => sql.push_str(" >= "),
196                        super::types::QueryOperator::LessThanOrEqual => sql.push_str(" <= "),
197                        super::types::QueryOperator::Like => sql.push_str(" LIKE "),
198                        super::types::QueryOperator::NotLike => sql.push_str(" NOT LIKE "),
199                        super::types::QueryOperator::In => {
200                            sql.push_str(" IN (");
201                            let placeholder_count = condition.values.len();
202                            if placeholder_count > 0 {
203                                let placeholders = Self::generate_sequential_placeholders(
204                                    param_counter,
205                                    placeholder_count,
206                                );
207                                sql.push_str(&placeholders);
208                                param_counter += placeholder_count;
209                            }
210                            sql.push(')');
211                            continue; // Skip the normal parameter handling
212                        }
213                        super::types::QueryOperator::NotIn => {
214                            sql.push_str(" NOT IN (");
215                            let placeholder_count = condition.values.len();
216                            if placeholder_count > 0 {
217                                let placeholders = Self::generate_sequential_placeholders(
218                                    param_counter,
219                                    placeholder_count,
220                                );
221                                sql.push_str(&placeholders);
222                                param_counter += placeholder_count;
223                            }
224                            sql.push(')');
225                            continue; // Skip the normal parameter handling
226                        }
227                        super::types::QueryOperator::IsNull => {
228                            sql.push_str(" IS NULL");
229                            continue;
230                        }
231                        super::types::QueryOperator::IsNotNull => {
232                            sql.push_str(" IS NOT NULL");
233                            continue;
234                        }
235                        super::types::QueryOperator::Between => {
236                            sql.push_str(&format!(
237                                " BETWEEN ${} AND ${}",
238                                param_counter,
239                                param_counter + 1
240                            ));
241                            param_counter += 2;
242                            continue;
243                        }
244                        super::types::QueryOperator::Raw => {
245                            // For raw SQL expressions, just add the value directly
246                            if let Some(ref value) = condition.value {
247                                if let serde_json::Value::String(raw_expr) = value {
248                                    sql.push(' ');
249                                    sql.push_str(raw_expr);
250                                }
251                            }
252                            continue;
253                        }
254                    }
255
256                    // Add parameter placeholder for regular operators
257                    sql.push_str(&format!("${}", param_counter));
258                    param_counter += 1;
259                }
260            }
261        }
262
263        // GROUP BY
264        if !self.group_by.is_empty() {
265            sql.push_str(" GROUP BY ");
266            for (i, column) in self.group_by.iter().enumerate() {
267                if i > 0 {
268                    sql.push_str(", ");
269                }
270                sql.push_str(column);
271            }
272        }
273
274        // HAVING
275        if !self.having_conditions.is_empty() {
276            sql.push_str(" HAVING ");
277            for (i, condition) in self.having_conditions.iter().enumerate() {
278                if i > 0 {
279                    sql.push_str(" AND ");
280                }
281                sql.push_str(&condition.column);
282
283                // Handle HAVING operators with proper parameter indexing
284                match condition.operator {
285                    super::types::QueryOperator::Equal => sql.push_str(" = "),
286                    super::types::QueryOperator::GreaterThan => sql.push_str(" > "),
287                    super::types::QueryOperator::LessThan => sql.push_str(" < "),
288                    _ => sql.push_str(" = "), // Default to equals
289                }
290
291                sql.push_str(&format!("${}", param_counter));
292                param_counter += 1;
293            }
294        }
295
296        // ORDER BY
297        if !self.order_by.is_empty() {
298            sql.push_str(" ORDER BY ");
299            for (i, (column, direction)) in self.order_by.iter().enumerate() {
300                if i > 0 {
301                    sql.push_str(", ");
302                }
303                sql.push_str(column);
304                match direction {
305                    super::types::OrderDirection::Asc => sql.push_str(" ASC"),
306                    super::types::OrderDirection::Desc => sql.push_str(" DESC"),
307                }
308            }
309        }
310
311        // LIMIT
312        if let Some(limit) = self.limit_count {
313            sql.push_str(" LIMIT ");
314            sql.push_str(&limit.to_string());
315        }
316
317        // OFFSET
318        if let Some(offset) = self.offset_value {
319            sql.push_str(" OFFSET ");
320            sql.push_str(&offset.to_string());
321        }
322    }
323}
324
325/// Query builder pool for reusing query builder instances to reduce allocations
326pub struct QueryBuilderPool {
327    pool: Arc<RwLock<Vec<QueryBuilder<()>>>>,
328    max_size: usize,
329}
330
331impl QueryBuilderPool {
332    pub fn new(max_size: usize) -> Self {
333        Self {
334            pool: Arc::new(RwLock::new(Vec::with_capacity(max_size))),
335            max_size,
336        }
337    }
338
339    /// Get a query builder from the pool or create a new one
340    pub fn acquire(&self) -> QueryBuilder<()> {
341        if let Ok(mut pool) = self.pool.write() {
342            if let Some(mut builder) = pool.pop() {
343                // Reset the builder to default state
344                builder.reset();
345                return builder;
346            }
347        }
348
349        // Create new builder if pool is empty
350        QueryBuilder::new()
351    }
352
353    /// Return a query builder to the pool
354    pub fn release(&self, builder: QueryBuilder<()>) {
355        if let Ok(mut pool) = self.pool.write() {
356            if pool.len() < self.max_size {
357                pool.push(builder);
358            }
359            // If pool is full, just drop the builder
360        }
361    }
362}
363
364impl<M> QueryBuilder<M> {
365    /// Reset query builder to default state for reuse
366    pub fn reset(&mut self) {
367        self.query_type = super::types::QueryType::Select;
368        self.select_fields.clear();
369        self.from_tables.clear();
370        self.insert_table = None;
371        self.update_table = None;
372        self.delete_table = None;
373        self.set_clauses.clear();
374        self.where_conditions.clear();
375        self.joins.clear();
376        self.order_by.clear();
377        self.group_by.clear();
378        self.having_conditions.clear();
379        self.limit_count = None;
380        self.offset_value = None;
381        self.distinct = false;
382    }
383}
384
385/// Global query builder pool instance
386static GLOBAL_QUERY_POOL: Lazy<QueryBuilderPool> = Lazy::new(|| {
387    QueryBuilderPool::new(100) // Pool of up to 100 query builders
388});
389
390/// Get a query builder from the global pool
391pub fn acquire_query_builder() -> QueryBuilder<()> {
392    GLOBAL_QUERY_POOL.acquire()
393}
394
395/// Return a query builder to the global pool
396pub fn release_query_builder(builder: QueryBuilder<()>) {
397    GLOBAL_QUERY_POOL.release(builder);
398}
399
400#[cfg(test)]
401mod tests {
402    use super::*;
403
404    #[test]
405    fn test_placeholder_caching() {
406        let placeholders1 = QueryBuilder::<()>::generate_placeholders_cached(3);
407        let placeholders2 = QueryBuilder::<()>::generate_placeholders_cached(3);
408
409        assert_eq!(placeholders1, "$1, $2, $3");
410        assert_eq!(placeholders1, placeholders2);
411    }
412
413    #[test]
414    fn test_query_builder_pool() {
415        let pool = QueryBuilderPool::new(2);
416
417        let builder1 = pool.acquire();
418        let builder2 = pool.acquire();
419
420        pool.release(builder1);
421        pool.release(builder2);
422
423        let builder3 = pool.acquire(); // Should reuse from pool
424        assert!(!builder3.from_tables.is_empty() || builder3.from_tables.is_empty());
425        // Basic check
426    }
427
428    #[test]
429    fn test_optimized_sql_generation() {
430        let query: QueryBuilder<()> = QueryBuilder::new()
431            .from("users")
432            .select("id, name, email")
433            .where_eq("active", "true");
434
435        let sql = query.to_sql_optimized();
436        assert!(sql.contains("SELECT"));
437        assert!(sql.contains("FROM users"));
438        assert!(sql.contains("WHERE"));
439    }
440}