reshape/migrations/
mod.rs1use crate::{
2 db::{Conn, Transaction},
3 schema::Schema,
4};
5use core::fmt::Debug;
6use serde::{Deserialize, Serialize};
7
8pub fn validate_sql_statement(sql: &str) -> Result<(), String> {
10 pg_query::parse(sql).map(|_| ()).map_err(|e| e.to_string())
11}
12
13pub fn validate_sql_expression(expr: &str) -> Result<(), String> {
15 let wrapped = format!("SELECT ({})", expr);
16 pg_query::parse(&wrapped).map(|_| ()).map_err(|e| e.to_string())
17}
18
19mod common;
21pub use common::Column;
22
23mod create_table;
24pub use create_table::CreateTable;
25
26mod alter_column;
27pub use alter_column::{AlterColumn, ColumnChanges};
28
29mod add_column;
30pub use add_column::AddColumn;
31
32mod remove_column;
33pub use remove_column::RemoveColumn;
34
35mod add_index;
36pub use add_index::{AddIndex, Index};
37
38mod remove_index;
39pub use remove_index::RemoveIndex;
40
41mod remove_table;
42pub use remove_table::RemoveTable;
43
44mod rename_table;
45pub use rename_table::RenameTable;
46
47mod create_enum;
48pub use create_enum::CreateEnum;
49
50mod remove_enum;
51pub use remove_enum::RemoveEnum;
52
53mod custom;
54pub use custom::Custom;
55
56mod add_foreign_key;
57pub use add_foreign_key::AddForeignKey;
58
59mod remove_foreign_key;
60pub use remove_foreign_key::RemoveForeignKey;
61
62#[derive(Serialize, Deserialize, Debug)]
63pub struct Migration {
64 pub name: String,
65 pub description: Option<String>,
66 pub actions: Vec<Box<dyn Action>>,
67}
68
69impl Migration {
70 pub fn new(name: impl Into<String>, description: Option<String>) -> Migration {
71 Migration {
72 name: name.into(),
73 description,
74 actions: vec![],
75 }
76 }
77
78 pub fn with_action(mut self, action: impl Action + 'static) -> Self {
79 self.actions.push(Box::new(action));
80 self
81 }
82}
83
84impl PartialEq for Migration {
85 fn eq(&self, other: &Self) -> bool {
86 self.name == other.name
87 }
88}
89
90impl Eq for Migration {}
91
92impl Clone for Migration {
93 fn clone(&self) -> Self {
94 let serialized = serde_json::to_string(self).unwrap();
95 serde_json::from_str(&serialized).unwrap()
96 }
97}
98
99pub struct MigrationContext {
100 migration_index: usize,
101 action_index: usize,
102 existing_schema_name: Option<String>,
103}
104
105impl MigrationContext {
106 pub fn new(
107 migration_index: usize,
108 action_index: usize,
109 existing_schema_name: Option<String>,
110 ) -> Self {
111 MigrationContext {
112 migration_index,
113 action_index,
114 existing_schema_name,
115 }
116 }
117
118 fn prefix(&self) -> String {
119 format!(
120 "__reshape_{:0>4}_{:0>4}",
121 self.migration_index, self.action_index
122 )
123 }
124
125 fn prefix_inverse(&self) -> String {
126 format!(
127 "__reshape_{:0>4}_{:0>4}",
128 1000 - self.migration_index,
129 1000 - self.action_index
130 )
131 }
132}
133
134#[typetag::serde(tag = "type")]
135pub trait Action: Debug {
136 fn describe(&self) -> String;
137 fn run(&self, ctx: &MigrationContext, db: &mut dyn Conn, schema: &Schema)
138 -> anyhow::Result<()>;
139 fn complete<'a>(
140 &self,
141 ctx: &MigrationContext,
142 db: &'a mut dyn Conn,
143 ) -> anyhow::Result<Option<Transaction<'a>>>;
144 fn update_schema(&self, ctx: &MigrationContext, schema: &mut Schema);
145 fn abort(&self, ctx: &MigrationContext, db: &mut dyn Conn) -> anyhow::Result<()>;
146
147 fn validate_sql(&self) -> Vec<(String, String, String)> {
149 vec![] }
151}