vibesql_types/sql_mode/config.rs
1/// MySQL-specific mode flags (similar to MySQL's sql_mode variable)
2///
3/// These flags control various MySQL-specific behaviors that differ from
4/// standard SQL or other database implementations.
5///
6/// ## References
7/// - [MySQL 8.0 sql_mode Documentation](https://dev.mysql.com/doc/refman/8.0/en/sql-mode.html)
8#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
9pub struct MySqlModeFlags {
10 /// Whether || acts as string concat (vs OR operator)
11 ///
12 /// Corresponds to MySQL PIPES_AS_CONCAT mode.
13 ///
14 /// - `true`: `'a' || 'b'` returns `'ab'` (string concatenation)
15 /// - `false` (default): `'a' || 'b'` returns `1` or `0` (logical OR)
16 pub pipes_as_concat: bool,
17
18 /// Whether " acts as identifier quote (vs string literal)
19 ///
20 /// Corresponds to MySQL ANSI_QUOTES mode.
21 ///
22 /// - `true`: `"col"` is an identifier (like backticks)
23 /// - `false` (default): `"col"` is a string literal (like single quotes)
24 pub ansi_quotes: bool,
25
26 /// Strict mode for type coercion and errors
27 ///
28 /// Corresponds to MySQL STRICT_TRANS_TABLES.
29 ///
30 /// - `true`: Strict type checking, errors on invalid data
31 /// - `false` (default): Permissive mode with warnings
32 pub strict_mode: bool,
33
34 /// Use SQLite division semantics (INTEGER / INTEGER → INTEGER)
35 ///
36 /// This flag is used when MySQL syntax is needed (e.g., `CAST...AS SIGNED`)
37 /// but SQLite division semantics are required for compatibility with test
38 /// suites that were generated using SQLite.
39 ///
40 /// - `true`: `5 / 2 = 2` (truncated integer division, SQLite behavior)
41 /// - `false` (default): `5 / 2 = 2.5` (exact decimal division, MySQL behavior)
42 ///
43 /// This is particularly useful for SQLLogicTest compatibility where tests
44 /// are tagged `onlyif mysql` for syntax reasons but expect SQLite division
45 /// results.
46 pub sqlite_division_semantics: bool,
47}
48
49impl MySqlModeFlags {
50 /// Create MySqlModeFlags with all default settings
51 pub fn new() -> Self {
52 Self::default()
53 }
54
55 /// Create MySqlModeFlags with PIPES_AS_CONCAT enabled
56 pub fn with_pipes_as_concat() -> Self {
57 Self { pipes_as_concat: true, ..Default::default() }
58 }
59
60 /// Create MySqlModeFlags with ANSI_QUOTES enabled
61 pub fn with_ansi_quotes() -> Self {
62 Self { ansi_quotes: true, ..Default::default() }
63 }
64
65 /// Create MySqlModeFlags with STRICT_MODE enabled
66 pub fn with_strict_mode() -> Self {
67 Self { strict_mode: true, ..Default::default() }
68 }
69
70 /// Create MySqlModeFlags with ANSI mode (combination of ANSI_QUOTES and PIPES_AS_CONCAT)
71 pub fn ansi() -> Self {
72 Self { pipes_as_concat: true, ansi_quotes: true, ..Default::default() }
73 }
74
75 /// Create MySqlModeFlags with SQLite division semantics enabled
76 ///
77 /// This is useful for SQLLogicTest compatibility where MySQL syntax is needed
78 /// but division should behave like SQLite (INTEGER / INTEGER → INTEGER).
79 pub fn with_sqlite_division_semantics() -> Self {
80 Self { sqlite_division_semantics: true, ..Default::default() }
81 }
82}
83
84#[cfg(test)]
85mod tests {
86 use super::*;
87
88 #[test]
89 fn test_default_mysql_flags() {
90 let flags = MySqlModeFlags::default();
91 assert!(!flags.pipes_as_concat); // || is OR by default
92 assert!(!flags.ansi_quotes); // " is string by default
93 assert!(!flags.strict_mode); // Permissive by default
94 }
95
96 #[test]
97 fn test_new_equals_default() {
98 assert_eq!(MySqlModeFlags::new(), MySqlModeFlags::default());
99 }
100
101 #[test]
102 fn test_with_pipes_as_concat() {
103 let flags = MySqlModeFlags::with_pipes_as_concat();
104 assert!(flags.pipes_as_concat);
105 assert!(!flags.ansi_quotes);
106 assert!(!flags.strict_mode);
107 }
108
109 #[test]
110 fn test_with_ansi_quotes() {
111 let flags = MySqlModeFlags::with_ansi_quotes();
112 assert!(!flags.pipes_as_concat);
113 assert!(flags.ansi_quotes);
114 assert!(!flags.strict_mode);
115 }
116
117 #[test]
118 fn test_with_strict_mode() {
119 let flags = MySqlModeFlags::with_strict_mode();
120 assert!(!flags.pipes_as_concat);
121 assert!(!flags.ansi_quotes);
122 assert!(flags.strict_mode);
123 }
124
125 #[test]
126 fn test_ansi_mode() {
127 let flags = MySqlModeFlags::ansi();
128 assert!(flags.pipes_as_concat);
129 assert!(flags.ansi_quotes);
130 assert!(!flags.strict_mode);
131 }
132
133 #[test]
134 fn test_flag_combinations() {
135 let flags = MySqlModeFlags {
136 pipes_as_concat: true,
137 ansi_quotes: true,
138 strict_mode: true,
139 sqlite_division_semantics: false,
140 };
141 assert!(flags.pipes_as_concat);
142 assert!(flags.ansi_quotes);
143 assert!(flags.strict_mode);
144 assert!(!flags.sqlite_division_semantics);
145 }
146
147 #[test]
148 fn test_with_sqlite_division_semantics() {
149 let flags = MySqlModeFlags::with_sqlite_division_semantics();
150 assert!(!flags.pipes_as_concat);
151 assert!(!flags.ansi_quotes);
152 assert!(!flags.strict_mode);
153 assert!(flags.sqlite_division_semantics);
154 }
155}