use std::collections::HashMap;
use crate::{build_update_sql, make_where};
use serde_json::Value;
pub fn make_update<K: AsRef<str> + Eq + std::hash::Hash>(
table: &str,
data: &HashMap<K, Value>,
wheres: Option<&[&str]>,
) -> String {
build_update_sql(table, data, wheres.map(make_where).as_deref())
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn test_update_no_where_none() {
let mut data = HashMap::new();
data.insert("active", json!(false));
let sql = make_update("users", &data, None);
assert_eq!(sql, r#"UPDATE "users" SET "active" = false"#);
}
#[test]
fn test_update_no_where_empty_slice() {
let mut data = HashMap::new();
data.insert("active", json!(true));
let sql = make_update("users", &data, Some(&[]));
assert_eq!(sql, r#"UPDATE "users" SET "active" = true"#);
}
#[test]
fn test_update_single_where() {
let mut data = HashMap::new();
data.insert("name", json!("Alice"));
let sql = make_update("users", &data, Some(&["id = 1"]));
assert_eq!(sql, r#"UPDATE "users" SET "name" = 'Alice' WHERE id = 1"#);
}
#[test]
fn test_update_single_where_numeric() {
let mut data = HashMap::new();
data.insert("score", json!(99));
let sql = make_update("players", &data, Some(&["player_id = 42"]));
assert_eq!(sql, r#"UPDATE "players" SET "score" = 99 WHERE player_id = 42"#);
}
#[test]
fn test_update_two_where_conditions() {
let mut data = HashMap::new();
data.insert("status", json!("inactive"));
let sql = make_update("users", &data, Some(&["active = 0", "age < 18"]));
assert_eq!(
sql,
r#"UPDATE "users" SET "status" = 'inactive' WHERE active = 0 AND age < 18"#
);
}
#[test]
fn test_update_three_where_conditions() {
let mut data = HashMap::new();
data.insert("role", json!("guest"));
let sql = make_update(
"users",
&data,
Some(&["active = 1", "age > 18", "verified = 1"]),
);
assert_eq!(
sql,
r#"UPDATE "users" SET "role" = 'guest' WHERE active = 1 AND age > 18 AND verified = 1"#
);
}
#[test]
fn test_update_string_value() {
let mut data = HashMap::new();
data.insert("name", json!("Bob"));
let sql = make_update("users", &data, Some(&["id = 7"]));
assert_eq!(sql, r#"UPDATE "users" SET "name" = 'Bob' WHERE id = 7"#);
}
#[test]
fn test_update_integer_value() {
let mut data = HashMap::new();
data.insert("age", json!(25));
let sql = make_update("users", &data, Some(&["id = 3"]));
assert_eq!(sql, r#"UPDATE "users" SET "age" = 25 WHERE id = 3"#);
}
#[test]
fn test_update_float_value() {
let mut data = HashMap::new();
data.insert("price", json!(9.99));
let sql = make_update("products", &data, Some(&["id = 10"]));
assert_eq!(sql, r#"UPDATE "products" SET "price" = 9.99 WHERE id = 10"#);
}
#[test]
fn test_update_bool_true_value() {
let mut data = HashMap::new();
data.insert("active", json!(true));
let sql = make_update("users", &data, Some(&["id = 1"]));
assert_eq!(sql, r#"UPDATE "users" SET "active" = true WHERE id = 1"#);
}
#[test]
fn test_update_bool_false_value() {
let mut data = HashMap::new();
data.insert("active", json!(false));
let sql = make_update("users", &data, Some(&["id = 2"]));
assert_eq!(sql, r#"UPDATE "users" SET "active" = false WHERE id = 2"#);
}
#[test]
fn test_update_null_value() {
let mut data = HashMap::new();
data.insert("deleted_at", json!(null));
let sql = make_update("users", &data, Some(&["id = 5"]));
assert_eq!(sql, r#"UPDATE "users" SET "deleted_at" = NULL WHERE id = 5"#);
}
#[test]
fn test_update_json_object_value() {
let mut data = HashMap::new();
data.insert("meta", json!({"role": "admin"}));
let sql = make_update("users", &data, Some(&["id = 1"]));
assert!(sql.starts_with(r#"UPDATE "users" SET "meta" = '"#));
assert!(sql.contains("role"));
assert!(sql.contains("admin"));
assert!(sql.ends_with("WHERE id = 1"));
}
#[test]
fn test_update_json_array_value() {
let mut data = HashMap::new();
data.insert("tags", json!(["rust", "sql"]));
let sql = make_update("posts", &data, Some(&["id = 3"]));
assert!(sql.starts_with(r#"UPDATE "posts" SET "tags" = '"#));
assert!(sql.contains("rust"));
assert!(sql.contains("sql"));
assert!(sql.ends_with("WHERE id = 3"));
}
#[test]
fn test_update_string_with_single_quote_escaped() {
let mut data = HashMap::new();
data.insert("name", json!("O'Brien"));
let sql = make_update("users", &data, Some(&["id = 1"]));
assert_eq!(sql, r#"UPDATE "users" SET "name" = 'O''Brien' WHERE id = 1"#);
}
#[test]
fn test_update_column_with_double_quote_escaped() {
let mut data = HashMap::new();
data.insert(r#"col"name"#, json!("value"));
let sql = make_update("users", &data, Some(&["id = 1"]));
assert!(sql.contains(r#""col""name""#));
}
#[test]
fn test_update_table_name_with_double_quote_escaped() {
let mut data = HashMap::new();
data.insert("x", json!(1));
let sql = make_update(r#"my"table"#, &data, Some(&["id = 1"]));
assert!(sql.starts_with(r#"UPDATE "my""table""#));
}
#[test]
fn test_update_table_name_is_trimmed() {
let mut data = HashMap::new();
data.insert("name", json!("Carol"));
let sql = make_update(" users ", &data, Some(&["id = 4"]));
assert_eq!(sql, r#"UPDATE "users" SET "name" = 'Carol' WHERE id = 4"#);
}
#[test]
fn test_update_where_whitespace_only_is_skipped() {
let mut data = HashMap::new();
data.insert("active", json!(true));
let sql = make_update("users", &data, Some(&[" "]));
assert_eq!(sql, r#"UPDATE "users" SET "active" = true"#);
}
#[test]
fn test_update_where_in_clause() {
let mut data = HashMap::new();
data.insert("status", json!("archived"));
let sql = make_update("posts", &data, Some(&["id IN (1, 2, 3)"]));
assert_eq!(
sql,
r#"UPDATE "posts" SET "status" = 'archived' WHERE id IN (1, 2, 3)"#
);
}
#[test]
fn test_update_where_is_null() {
let mut data = HashMap::new();
data.insert("processed", json!(true));
let sql = make_update("events", &data, Some(&["processed_at IS NULL"]));
assert_eq!(
sql,
r#"UPDATE "events" SET "processed" = true WHERE processed_at IS NULL"#
);
}
#[test]
fn test_update_multiple_fields_with_where() {
let mut data = HashMap::new();
data.insert("name", json!("Dave"));
data.insert("age", json!(40));
let sql = make_update("users", &data, Some(&["id = 9"]));
let v1 = r#"UPDATE "users" SET "name" = 'Dave' , "age" = 40 WHERE id = 9"#;
let v2 = r#"UPDATE "users" SET "age" = 40 , "name" = 'Dave' WHERE id = 9"#;
assert!(sql == v1 || sql == v2, "Unexpected SQL: {sql}");
}
#[test]
fn test_update_multiple_fields_no_where() {
let mut data = HashMap::new();
data.insert("x", json!(1));
data.insert("y", json!(2));
let sql = make_update("coords", &data, None);
assert!(sql.starts_with(r#"UPDATE "coords" SET "#));
assert!(sql.contains(r#""x" = 1"#));
assert!(sql.contains(r#""y" = 2"#));
assert!(!sql.contains("WHERE"));
}
#[test]
fn test_update_str_keys() {
let mut data: HashMap<&str, Value> = HashMap::new();
data.insert("email", json!("test@example.com"));
let sql = make_update("users", &data, Some(&["id = 10"]));
assert_eq!(
sql,
r#"UPDATE "users" SET "email" = 'test@example.com' WHERE id = 10"#
);
}
#[test]
fn test_update_always_starts_with_update() {
let mut data = HashMap::new();
data.insert("x", json!(1));
let sql = make_update("t", &data, None);
assert!(sql.starts_with("UPDATE "));
}
#[test]
fn test_update_always_contains_set() {
let mut data = HashMap::new();
data.insert("x", json!(1));
let sql = make_update("t", &data, Some(&["id = 1"]));
assert!(sql.contains(" SET "));
}
}