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