1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
//! Execution limits and safeguards
//!
//! This module defines limits to prevent infinite loops, stack overflow, and runaway queries.
//! These limits follow industry best practices established by SQLite and other production
//! databases.
//!
//! ## Design Philosophy
//!
//! These limits serve multiple purposes:
//! 1. **Safety**: Prevent crashes from stack overflow or infinite recursion
//! 2. **Performance**: Kill runaway queries that would hang the system
//! 3. **Debugging**: Provide clear error messages when limits are exceeded
//! 4. **Compatibility**: Match SQLite's limit philosophy for consistency
//!
//! ## Limit Values
//!
//! Default values are chosen conservatively to allow legitimate queries while catching
//! pathological cases. They can be adjusted based on real-world usage patterns.
/// Maximum depth of expression tree evaluation (subqueries, nested expressions)
///
/// SQLite uses 1000 for SQLITE_MAX_EXPR_DEPTH
/// We use a more conservative 200 to prevent stack overflow
///
/// This limit is based on Rust's default stack size (typically 2MB on most platforms).
/// Testing shows that expression depths beyond ~200 can cause stack overflow during
/// evaluation even with depth tracking, as each recursive call consumes stack space.
///
/// This prevents stack overflow from deeply nested:
/// - Subqueries (IN, EXISTS, scalar subqueries)
/// - Arithmetic expressions
/// - Function calls
/// - Boolean logic (AND/OR chains)
///
/// SQLite uses 1000 for SQLITE_MAX_EXPR_DEPTH by default.
/// We match this value for better compatibility.
pub const MAX_EXPRESSION_DEPTH: usize = 1000;
/// Maximum number of compound SELECT terms
///
/// SQLite uses 500 for SQLITE_MAX_COMPOUND_SELECT
/// We match this value
///
/// This prevents stack overflow from deeply nested UNION/INTERSECT/EXCEPT chains
pub const MAX_COMPOUND_SELECT: usize = 500;
/// Maximum number of rows to process in a single query execution
///
/// This prevents infinite loops in:
/// - WHERE clause evaluation
/// - Join operations
/// - Aggregate processing
///
/// Set high enough for legitimate large queries (10 million rows)
pub const MAX_ROWS_PROCESSED: usize = 10_000_000;
/// Maximum execution time for a single query (in seconds)
///
/// This is a soft timeout - checked periodically, not enforced precisely
///
/// Set to 300 seconds (5 minutes) to allow complex multi-table joins to complete.
/// With predicate pushdown optimization (#1122), memory usage is under control (6.48 GB
/// vs 73+ GB before). Join reordering optimization is now enabled for 3-8 table joins,
/// which should significantly reduce execution time for complex analytical queries.
///
/// If join reordering proves effective, consider reducing this timeout to a lower value
/// (e.g., 60-120 seconds) to catch runaway queries more quickly.
pub const MAX_QUERY_EXECUTION_SECONDS: u64 = 300;
/// Maximum number of iterations in a single loop (e.g., WHERE filtering)
///
/// This catches infinite loops in iteration logic
/// Should be higher than MAX_ROWS_PROCESSED to avoid false positives
pub const MAX_LOOP_ITERATIONS: usize = 50_000_000;
/// Maximum number of iterations for recursive CTEs
///
/// SQLite's default is 1000, but it can be increased via sqlite3_limit().
/// We use a much higher default (1 million) because:
/// 1. Our recursive CTE implementation is iterative, not stack-recursive
/// 2. Many legitimate use cases need more than 1000 iterations (e.g., date ranges)
/// 3. Memory limits already protect against truly runaway queries
/// 4. This matches SQLite's practical behavior where the limit is configurable
///
/// The limit still catches truly infinite loops while allowing legitimate deep recursion.
pub const MAX_RECURSIVE_CTE_ITERATIONS: usize = 1_000_000;
/// Maximum number of tables in a single join operation
///
/// SQLite uses 64 for SQLITE_LIMIT_COMPOUND_SELECT-like join limits
/// This prevents exponential cost from very large multi-table joins
/// and catches queries that would likely be impractical anyway.
///
/// Error message: "at most 64 tables in a join"
pub const MAX_TABLES_IN_JOIN: usize = 64;
/// Maximum memory usage per query execution
///
/// This prevents:
/// - Cartesian product explosions
/// - Exponential intermediate result growth
/// - System memory exhaustion
/// - Swapping/OOM kills
///
/// Large enough for legitimate queries, small enough to catch pathological cases
///
/// WASM targets have more constrained memory (typically 2-4 GB addressable),
/// so we use smaller limits there to avoid overflow
pub const MAX_MEMORY_BYTES: usize = 1 * 1024 * 1024 * 1024; // 1 GB for WASM
pub const MAX_MEMORY_BYTES: usize = 10 * 1024 * 1024 * 1024; // 10 GB for native
/// Warning threshold - log when memory exceeds this
pub const MEMORY_WARNING_BYTES: usize = 512 * 1024 * 1024; // 512 MB for WASM
pub const MEMORY_WARNING_BYTES: usize = 5 * 1024 * 1024 * 1024; // 5 GB for native