1// Copyright 2024 New Vector Ltd.
2//
3// SPDX-License-Identifier: AGPL-3.0-only
4// Please see LICENSE in the repository root for full details.
56//! # MAS Database Checks
7//!
8//! This module provides safety checks to run against a MAS database before
9//! running the Synapse-to-MAS migration.
1011use thiserror::Error;
12use thiserror_ext::ContextInto;
13use tracing::Instrument as _;
1415use super::{MAS_TABLES_AFFECTED_BY_MIGRATION, is_syn2mas_in_progress, locking::LockedMasDatabase};
1617#[derive(Debug, Error, ContextInto)]
18pub enum Error {
19#[error("the MAS database is not empty: rows found in at least `{table}`")]
20MasDatabaseNotEmpty { table: &'static str },
2122#[error("query against {table} failed — is this actually a MAS database?")]
23MaybeNotMas {
24#[source]
25source: sqlx::Error,
26 table: &'static str,
27 },
2829#[error(transparent)]
30Sqlx(#[from] sqlx::Error),
3132#[error("unable to check if syn2mas is already in progress")]
33UnableToCheckInProgress(#[source] super::Error),
34}
3536/// Check that a MAS database is ready for being migrated to.
37///
38/// Concretely, this checks that the database is empty.
39///
40/// If syn2mas is already in progress on this database, the checks are skipped.
41///
42/// # Errors
43///
44/// Errors are returned under the following circumstances:
45///
46/// - If any database access error occurs.
47/// - If any MAS tables involved in the migration are not empty.
48/// - If we can't check whether syn2mas is already in progress on this database
49/// or not.
50#[tracing::instrument(name = "syn2mas.mas_pre_migration_checks", skip_all)]
51pub async fn mas_pre_migration_checks(mas_connection: &mut LockedMasDatabase) -> Result<(), Error> {
52if is_syn2mas_in_progress(mas_connection.as_mut())
53 .await
54.map_err(Error::UnableToCheckInProgress)?
55{
56// syn2mas already in progress, so we already performed the checks
57return Ok(());
58 }
5960// Check that the database looks like a MAS database and that it is also an
61 // empty database.
6263for &table in MAS_TABLES_AFFECTED_BY_MIGRATION {
64let query = format!("SELECT 1 AS dummy FROM {table} LIMIT 1");
65let span = tracing::info_span!("db.query", db.query.text = query);
66let row_present = sqlx::query(&query)
67 .fetch_optional(mas_connection.as_mut())
68 .instrument(span)
69 .await
70.into_maybe_not_mas(table)?
71.is_some();
7273if row_present {
74return Err(Error::MasDatabaseNotEmpty { table });
75 }
76 }
7778Ok(())
79}