@@ -0,0 +1,91 @@
1+use super::{Action, MigrationContext};
3+ db::{Conn, Transaction},
6+use anyhow::{anyhow, Context};
7+use serde::{Deserialize, Serialize};
9+#[derive(Serialize, Deserialize, Debug)]
10+pub struct RemoveForeignKey {
15+#[typetag::serde(name = "remove_foreign_key")]
16+impl Action for RemoveForeignKey {
17+ fn describe(&self) -> String {
19+ "Removing foreign key \"{}\" from table \"{}\"",
20+ self.foreign_key, self.table
26+ _ctx: &MigrationContext,
29+ ) -> anyhow::Result<()> {
30+ // The foreign key is only removed once the migration is completed.
31+ // Removing it earlier would be hard/undesirable for several reasons:
32+ // - Postgres doesn't have an easy way to temporarily disable a foreign key check.
33+ // If it did, we could disable the FK for the new schema.
34+ // - Even if we could, it probably wouldn't be a good idea as it would cause temporary
35+ // inconsistencies for the old schema which still expects the FK to hold.
36+ // - For the same reason, we can't remove the FK when the migration is first applied.
37+ // If the migration was to be aborted, then the FK would have to be recreated with
38+ // the risk that it would no longer be valid.
40+ // Ensure foreign key exists
41+ let table = schema.get_table(db, &self.table)?;
45+ SELECT constraint_name
46+ FROM information_schema.table_constraints
48+ constraint_type = 'FOREIGN KEY' AND
49+ table_name = '{table_name}' AND
50+ constraint_name = '{foreign_key}'
52+ table_name = table.real_name,
53+ foreign_key = self.foreign_key,
55+ .context("failed to check for foreign key")?
60+ "no foreign key \"{}\" exists on table \"{}\"",
71+ _ctx: &MigrationContext,
72+ db: &'a mut dyn Conn,
73+ ) -> anyhow::Result<Option<Transaction<'a>>> {
77+ DROP CONSTRAINT IF EXISTS {foreign_key}
80+ foreign_key = self.foreign_key,
82+ .context("failed to remove foreign key")?;
86+ fn update_schema(&self, _ctx: &MigrationContext, _schema: &mut Schema) {}
88+ fn abort(&self, _ctx: &MigrationContext, _db: &mut dyn Conn) -> anyhow::Result<()> {