tag2upload_service_manager/
db_schema.rs1use crate::prelude::*;
12use db_migration::MigrationData;
13
14const SCHEMA_BASE: &str = include_str!("schema-base.sql");
15
16pub const SCHEMA_VERSION: SchemaVersion = 2;
17
18fn add_stats_triggers(
19 s: &mut String,
20 base_conn: &rusqlite::Connection,
21) -> Result<(), SchemaGenerationError> {
22
23 let table = "stats_by_shown_status";
36 let mut trigs = BTreeMap::<_, Vec<String>>::new();
37
38 for (del_ins, old_new, delta) in [
39 ("DELETE", "OLD", "- 1"),
40 ("INSERT", "NEW", "+ 1"),
41 ] {
42 for (trig_kw, trig_cond) in [
43 (del_ins, ""),
44 ("UPDATE", " OF status, duplicate_of"),
45 ] {
46 trigs.entry(
47 (trig_kw, trig_cond)
48 ).or_default().push(format!(
49" UPDATE {table}
50 SET n_jobs = n_jobs {delta}
51 WHERE shown_status = IIF(
52 {old_new}.duplicate_of IS NOT NULL,
53 'Duplicate',
54 {old_new}.status
55 );
56"
57 ));
58 }
59 }
60
61 for ((trig_kw, trig_cond), bodies) in trigs {
62 let trig_name = format!("{table}_{trig_kw}")
63 .to_ascii_lowercase();
64
65 writeln!(s,
66"CREATE TRIGGER IF NOT EXISTS {trig_name}
67 AFTER {trig_kw}{trig_cond}
68 ON jobs
69 FOR EACH ROW
70 BEGIN"
71 )?;
72 for b in bodies {
73 write!(s, "{b}")?;
74 }
75 writeln!(s,
76" END;"
77 )?;
78 }
79
80 {
81 let cols = db_support::table_column_names(base_conn, "job_history")?
86 .into_iter().filter(|s| s != "histent")
87 .collect_vec();
88 let cols = || cols.iter().map(|s| &**s);
89 let col_names = cols()
90 .intersperse(", ").collect::<String>();
91 let col_values = cols().map(|c| format!("OLD.{c}"))
92 .intersperse(", ".into()).collect::<String>();
93 writeln!(s,
94"CREATE TRIGGER IF NOT EXISTS job_history_trigger
95 AFTER UPDATE
96 OF {col_names}
97 ON jobs
98 FOR EACH ROW
99 BEGIN
100 INSERT INTO job_history ({col_names})
101 VALUES ({col_values});
102 END;"
103 )?;
104 }
105
106 Ok(())
107}
108
109pub fn migration_data() -> MigrationData {
110 #[allow(unused)] use db_migration::{Migration, Exception as E};
112
113 MigrationData {
114 schemas: vec![
115 db_migration::Schema {
116 version: SCHEMA_VERSION,
117 schema: db_schema::schema(),
118 },
119 ],
120 migrations: vec![
121 Migration {
122 old_version: 1,
123 new_version: 2,
124 exceptions: vec![
125 E::ReplacementColumnValue {
126 table: "jobs",
127 col: "retry_earliest",
128 val_sql: "0".into(),
129 },
130 E::ReplacementColumnValue {
131 table: "jobs",
132 col: "retry_salient_count",
133 val_sql: "0".into(),
134 },
135 E::ReplacementColumnValue {
136 table: "job_history",
137 col: "retry_salient_count",
138 val_sql: "0".into(),
139 },
140 ],
141 },
142 ],
143 }
144}
145
146pub fn schema() -> String {
147 (|| {
148 let mut s = String::new();
149
150 write!(s, "{SCHEMA_BASE}")?;
151
152 let base_conn = rusqlite::Connection::open_in_memory()
153 .db_context("open")?;
154 base_conn.execute_batch(&s)
155 .db_context("execute base")?;
156
157 add_stats_triggers(&mut s, &base_conn)?;
158
159 Ok::<_, SchemaGenerationError>(s)
160 })().expect("failed to construct dynamic parts of schema string")
161}