vespertide_query/sql/
remap_enum_values.rs1use std::collections::BTreeMap;
15
16use super::helpers::quote_ident;
17use super::types::{BuiltQuery, DatabaseBackend, RawSql};
18use crate::error::QueryError;
19
20pub fn build_remap_enum_values(
25 backend: DatabaseBackend,
26 table: &str,
27 column: &str,
28 mapping: &BTreeMap<i64, i64>,
29) -> Result<Vec<BuiltQuery>, QueryError> {
30 if mapping.is_empty() {
31 return Ok(vec![]);
32 }
33 let qt = quote_ident(table, backend);
34 let qc = quote_ident(column, backend);
35 let case_arms: Vec<String> = mapping
36 .iter()
37 .map(|(old, new)| format!("WHEN {qc} = {old} THEN {new}"))
38 .collect();
39 let in_list: Vec<String> = mapping.keys().map(i64::to_string).collect();
40 let sql = format!(
41 "UPDATE {qt} SET {qc} = CASE {arms} END WHERE {qc} IN ({in_list})",
42 arms = case_arms.join(" "),
43 in_list = in_list.join(", "),
44 );
45 Ok(vec![BuiltQuery::Raw(RawSql::uniform(sql))])
46}
47
48#[cfg(test)]
49mod tests {
50 use super::*;
51 use insta::{assert_snapshot, with_settings};
52
53 fn run(backend: DatabaseBackend, mapping: &[(i64, i64)]) -> String {
54 let map: BTreeMap<i64, i64> = mapping.iter().copied().collect();
55 build_remap_enum_values(backend, "tickets", "priority", &map)
56 .expect("supported")
57 .iter()
58 .map(|q| q.build(backend))
59 .collect::<Vec<_>>()
60 .join(";\n")
61 }
62
63 fn snap(name: &str, sql: &str) {
64 with_settings!(
69 { snapshot_path => "./snapshots", snapshot_suffix => format!("remap_{}", name) },
70 { assert_snapshot!(sql); }
71 );
72 }
73
74 macro_rules! remap_all_backends {
77 ($name:ident, $mapping:expr) => {
78 #[test]
79 fn $name() {
80 for (backend, tag) in [
81 (DatabaseBackend::Postgres, "postgres"),
82 (DatabaseBackend::MySql, "mysql"),
83 (DatabaseBackend::Sqlite, "sqlite"),
84 ] {
85 let sql = run(backend, $mapping);
86 snap(&format!("{}_{}", stringify!($name), tag), &sql);
87 }
88 }
89 };
90 }
91
92 remap_all_backends!(single_pair, &[(5_i64, 100_i64)]);
94
95 remap_all_backends!(multi_pair, &[(5_i64, 100_i64), (10_i64, 200_i64)]);
97
98 remap_all_backends!(value_swap, &[(5_i64, 10_i64), (10_i64, 5_i64)]);
101
102 remap_all_backends!(negative_values, &[(-1_i64, 0_i64), (0_i64, 1_i64)]);
104
105 #[test]
106 fn empty_mapping_emits_no_query() {
107 let queries = build_remap_enum_values(
108 DatabaseBackend::Postgres,
109 "tickets",
110 "priority",
111 &BTreeMap::new(),
112 )
113 .expect("empty mapping is a valid no-op");
114 assert!(queries.is_empty());
115 }
116}