use std::collections::BTreeMap;
use std::fmt::Write as _;
use super::helpers::quote_ident;
use super::types::{BuiltQuery, DatabaseBackend, RawSql};
use crate::error::QueryError;
pub fn build_remap_enum_values(
backend: DatabaseBackend,
table: &str,
column: &str,
mapping: &BTreeMap<i64, i64>,
) -> Result<Vec<BuiltQuery>, QueryError> {
if mapping.is_empty() {
return Ok(vec![]);
}
let qt = quote_ident(table, backend);
let qc = quote_ident(column, backend);
let mut arms = String::new();
let mut in_list = String::new();
for (old, new) in mapping {
if !arms.is_empty() {
arms.push(' ');
}
let _ = write!(arms, "WHEN {qc} = {old} THEN {new}");
if !in_list.is_empty() {
in_list.push_str(", ");
}
let _ = write!(in_list, "{old}");
}
let sql = format!("UPDATE {qt} SET {qc} = CASE {arms} END WHERE {qc} IN ({in_list})");
Ok(vec![BuiltQuery::Raw(RawSql::uniform(sql))])
}
#[cfg(test)]
mod tests {
use super::*;
use crate::test_support::backend_tag;
use insta::{assert_snapshot, with_settings};
fn run(backend: DatabaseBackend, mapping: &[(i64, i64)]) -> String {
let map: BTreeMap<i64, i64> = mapping.iter().copied().collect();
build_remap_enum_values(backend, "tickets", "priority", &map)
.expect("supported")
.iter()
.map(|q| q.build(backend))
.collect::<Vec<_>>()
.join(";\n")
}
fn snap(name: &str, sql: &str) {
with_settings!(
{ snapshot_path => "./snapshots", snapshot_suffix => format!("remap_{}", name) },
{ assert_snapshot!(sql); }
);
}
macro_rules! remap_all_backends {
($name:ident, $mapping:expr) => {
#[test]
fn $name() {
for backend in [
DatabaseBackend::Postgres,
DatabaseBackend::MySql,
DatabaseBackend::Sqlite,
] {
let tag = backend_tag(backend);
let sql = run(backend, $mapping);
snap(&format!("{}_{}", stringify!($name), tag), &sql);
}
}
};
}
remap_all_backends!(single_pair, &[(5_i64, 100_i64)]);
remap_all_backends!(multi_pair, &[(5_i64, 100_i64), (10_i64, 200_i64)]);
remap_all_backends!(value_swap, &[(5_i64, 10_i64), (10_i64, 5_i64)]);
remap_all_backends!(negative_values, &[(-1_i64, 0_i64), (0_i64, 1_i64)]);
#[test]
fn empty_mapping_emits_no_query() {
let queries = build_remap_enum_values(
DatabaseBackend::Postgres,
"tickets",
"priority",
&BTreeMap::new(),
)
.expect("empty mapping is a valid no-op");
assert!(queries.is_empty());
}
}