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}