#[cfg(test)]
mod tests {
use vibesql_parser::Parser;
use vibesql_storage::Database;
use vibesql_types::SqlMode;
use crate::{SchemaExecutor, SelectExecutor};
fn execute_set_variable(
db: &mut Database,
sql: &str,
) -> Result<String, crate::errors::ExecutorError> {
let stmt = Parser::parse_sql(sql).expect("Failed to parse SET statement");
if let vibesql_ast::Statement::SetVariable(set_stmt) = stmt {
SchemaExecutor::execute_set_variable(&set_stmt, db)
} else {
panic!("Expected SetVariable statement, got {:?}", stmt);
}
}
fn select_single_value(db: &Database, sql: &str) -> vibesql_types::SqlValue {
let stmt = Parser::parse_sql(sql).expect("Failed to parse SELECT");
if let vibesql_ast::Statement::Select(select_stmt) = stmt {
let executor = SelectExecutor::new(db);
let rows = executor.execute(&select_stmt).expect("Failed to execute SELECT");
rows[0].values[0].clone()
} else {
panic!("Expected Select statement");
}
}
#[test]
fn test_set_sql_mode_mysql() {
let mut db = Database::new();
assert!(matches!(db.sql_mode(), SqlMode::MySQL { .. }));
execute_set_variable(&mut db, "SET sql_mode = 'sqlite'").unwrap();
assert!(matches!(db.sql_mode(), SqlMode::SQLite));
let result = execute_set_variable(&mut db, "SET sql_mode = 'mysql'").unwrap();
assert!(result.contains("mysql"));
assert!(matches!(db.sql_mode(), SqlMode::MySQL { .. }));
}
#[test]
fn test_set_sql_mode_sqlite() {
let mut db = Database::new();
let result = execute_set_variable(&mut db, "SET sql_mode = 'sqlite'").unwrap();
assert!(result.contains("sqlite"));
assert!(matches!(db.sql_mode(), SqlMode::SQLite));
}
#[test]
fn test_set_sql_mode_case_insensitive() {
let mut db = Database::new();
execute_set_variable(&mut db, "SET sql_mode = 'MYSQL'").unwrap();
assert!(matches!(db.sql_mode(), SqlMode::MySQL { .. }));
execute_set_variable(&mut db, "SET sql_mode = 'SQLITE'").unwrap();
assert!(matches!(db.sql_mode(), SqlMode::SQLite));
execute_set_variable(&mut db, "SET sql_mode = 'MySql'").unwrap();
assert!(matches!(db.sql_mode(), SqlMode::MySQL { .. }));
}
#[test]
fn test_set_sql_mode_invalid() {
let mut db = Database::new();
let result = execute_set_variable(&mut db, "SET sql_mode = 'invalid!@#'");
assert!(result.is_err());
let err = result.unwrap_err();
let err_str = err.to_string();
assert!(err_str.contains("Unknown SQL mode"));
}
#[test]
fn test_set_sql_mode_mysql_flags() {
let mut db = Database::new();
let result = execute_set_variable(
&mut db,
"SET sql_mode = 'STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE'",
);
assert!(result.is_ok());
match db.sql_mode() {
SqlMode::MySQL { flags } => {
assert!(flags.strict_mode);
}
_ => panic!("Expected MySQL mode"),
}
}
#[test]
fn test_set_sql_mode_leading_comma() {
let mut db = Database::new();
let result = execute_set_variable(
&mut db,
"SET sql_mode = ',STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE'",
);
assert!(result.is_ok());
match db.sql_mode() {
SqlMode::MySQL { flags } => {
assert!(flags.strict_mode);
}
_ => panic!("Expected MySQL mode"),
}
}
#[test]
fn test_select_session_variable_sql_mode() {
let mut db = Database::new();
execute_set_variable(&mut db, "SET sql_mode = 'sqlite'").unwrap();
let value = select_single_value(&db, "SELECT @@sql_mode");
match value {
vibesql_types::SqlValue::Varchar(s) => {
assert!(s.to_lowercase().contains("sqlite"));
}
_ => panic!("Expected Varchar value, got {:?}", value),
}
}
#[test]
fn test_division_behavior_changes_with_mode() {
let mut db = Database::new();
execute_set_variable(&mut db, "SET sql_mode = 'mysql'").unwrap();
let mysql_result = select_single_value(&db, "SELECT 5 / 2");
match mysql_result {
vibesql_types::SqlValue::Numeric(n) => {
let f: f64 = n.to_string().parse().unwrap();
assert!((f - 2.5).abs() < 0.0001, "MySQL division 5/2 should be 2.5, got {}", f);
}
vibesql_types::SqlValue::Real(f) => {
assert!((f - 2.5).abs() < 0.0001, "MySQL division 5/2 should be 2.5, got {}", f);
}
other => {
let s = other.to_string();
let f: f64 = s.parse().unwrap_or(0.0);
assert!((f - 2.5).abs() < 0.0001, "MySQL division 5/2 should be 2.5, got {}", s);
}
}
execute_set_variable(&mut db, "SET sql_mode = 'sqlite'").unwrap();
let sqlite_result = select_single_value(&db, "SELECT 5 / 2");
match sqlite_result {
vibesql_types::SqlValue::Integer(i) => {
assert_eq!(i, 2, "SQLite division 5/2 should be 2, got {}", i);
}
other => {
let s = other.to_string();
let i: i64 = s.parse().unwrap_or(0);
assert_eq!(i, 2, "SQLite division 5/2 should be 2, got {}", s);
}
}
}
#[test]
fn test_set_sql_mode_variable_name_case_insensitive() {
let mut db = Database::new();
execute_set_variable(&mut db, "SET SQL_MODE = 'sqlite'").unwrap();
assert!(matches!(db.sql_mode(), SqlMode::SQLite));
execute_set_variable(&mut db, "SET Sql_Mode = 'mysql'").unwrap();
assert!(matches!(db.sql_mode(), SqlMode::MySQL { .. }));
}
#[test]
fn test_cast_float_to_integer_mysql_rounds() {
let mut db = Database::new();
execute_set_variable(&mut db, "SET sql_mode = 'mysql'").unwrap();
let result = select_single_value(&db, "SELECT CAST(5.7 AS SIGNED)");
match result {
vibesql_types::SqlValue::Bigint(n) => {
assert_eq!(n, 6, "MySQL: CAST(5.7 AS SIGNED) should be 6, got {}", n)
}
vibesql_types::SqlValue::Integer(n) => {
assert_eq!(n, 6, "MySQL: CAST(5.7 AS SIGNED) should be 6, got {}", n)
}
other => panic!("Expected integer type, got {:?}", other),
}
let result = select_single_value(&db, "SELECT CAST(5.4 AS SIGNED)");
match result {
vibesql_types::SqlValue::Bigint(n) => {
assert_eq!(n, 5, "MySQL: CAST(5.4 AS SIGNED) should be 5, got {}", n)
}
vibesql_types::SqlValue::Integer(n) => {
assert_eq!(n, 5, "MySQL: CAST(5.4 AS SIGNED) should be 5, got {}", n)
}
other => panic!("Expected integer type, got {:?}", other),
}
let result = select_single_value(&db, "SELECT CAST(5.5 AS SIGNED)");
match result {
vibesql_types::SqlValue::Bigint(n) => {
assert_eq!(n, 6, "MySQL: CAST(5.5 AS SIGNED) should be 6, got {}", n)
}
vibesql_types::SqlValue::Integer(n) => {
assert_eq!(n, 6, "MySQL: CAST(5.5 AS SIGNED) should be 6, got {}", n)
}
other => panic!("Expected integer type, got {:?}", other),
}
let result = select_single_value(&db, "SELECT CAST(-5.7 AS SIGNED)");
match result {
vibesql_types::SqlValue::Bigint(n) => {
assert_eq!(n, -6, "MySQL: CAST(-5.7 AS SIGNED) should be -6, got {}", n)
}
vibesql_types::SqlValue::Integer(n) => {
assert_eq!(n, -6, "MySQL: CAST(-5.7 AS SIGNED) should be -6, got {}", n)
}
other => panic!("Expected integer type, got {:?}", other),
}
}
#[test]
fn test_cast_float_to_integer_sqlite_truncates() {
let mut db = Database::new();
execute_set_variable(&mut db, "SET sql_mode = 'sqlite'").unwrap();
let result = select_single_value(&db, "SELECT CAST(5.7 AS INTEGER)");
match result {
vibesql_types::SqlValue::Integer(n) => {
assert_eq!(n, 5, "SQLite: CAST(5.7 AS INTEGER) should be 5, got {}", n)
}
vibesql_types::SqlValue::Bigint(n) => {
assert_eq!(n, 5, "SQLite: CAST(5.7 AS INTEGER) should be 5, got {}", n)
}
other => panic!("Expected integer type, got {:?}", other),
}
let result = select_single_value(&db, "SELECT CAST(5.4 AS INTEGER)");
match result {
vibesql_types::SqlValue::Integer(n) => {
assert_eq!(n, 5, "SQLite: CAST(5.4 AS INTEGER) should be 5, got {}", n)
}
vibesql_types::SqlValue::Bigint(n) => {
assert_eq!(n, 5, "SQLite: CAST(5.4 AS INTEGER) should be 5, got {}", n)
}
other => panic!("Expected integer type, got {:?}", other),
}
let result = select_single_value(&db, "SELECT CAST(5.9 AS INTEGER)");
match result {
vibesql_types::SqlValue::Integer(n) => {
assert_eq!(n, 5, "SQLite: CAST(5.9 AS INTEGER) should be 5, got {}", n)
}
vibesql_types::SqlValue::Bigint(n) => {
assert_eq!(n, 5, "SQLite: CAST(5.9 AS INTEGER) should be 5, got {}", n)
}
other => panic!("Expected integer type, got {:?}", other),
}
let result = select_single_value(&db, "SELECT CAST(-5.7 AS INTEGER)");
match result {
vibesql_types::SqlValue::Integer(n) => {
assert_eq!(n, -5, "SQLite: CAST(-5.7 AS INTEGER) should be -5, got {}", n)
}
vibesql_types::SqlValue::Bigint(n) => {
assert_eq!(n, -5, "SQLite: CAST(-5.7 AS INTEGER) should be -5, got {}", n)
}
other => panic!("Expected integer type, got {:?}", other),
}
}
#[test]
fn test_cast_mode_switching() {
let mut db = Database::new();
execute_set_variable(&mut db, "SET sql_mode = 'mysql'").unwrap();
let result = select_single_value(&db, "SELECT CAST(5.7 AS SIGNED)");
match result {
vibesql_types::SqlValue::Bigint(n) | vibesql_types::SqlValue::Integer(n) => {
assert_eq!(n, 6, "MySQL mode should round 5.7 to 6")
}
other => panic!("Expected integer type, got {:?}", other),
}
execute_set_variable(&mut db, "SET sql_mode = 'sqlite'").unwrap();
let result = select_single_value(&db, "SELECT CAST(5.7 AS INTEGER)");
match result {
vibesql_types::SqlValue::Integer(n) | vibesql_types::SqlValue::Bigint(n) => {
assert_eq!(n, 5, "SQLite mode should truncate 5.7 to 5")
}
other => panic!("Expected integer type, got {:?}", other),
}
execute_set_variable(&mut db, "SET sql_mode = 'mysql'").unwrap();
let result = select_single_value(&db, "SELECT CAST(5.7 AS SIGNED)");
match result {
vibesql_types::SqlValue::Bigint(n) | vibesql_types::SqlValue::Integer(n) => {
assert_eq!(n, 6, "MySQL mode should round 5.7 to 6")
}
other => panic!("Expected integer type, got {:?}", other),
}
}
}