From 9a320171e52d12e51cd0119b1c0065e47019170f Mon Sep 17 00:00:00 2001 From: BlackDex Date: Fri, 9 Jan 2026 19:18:22 +0100 Subject: [PATCH] Force MySQL to use correct name and collation This should force all MySQL/MariaDB connections to use the correct names and collation to be `utf8mb4` and `utf8mb4_unicode_ci`. It will not fix current tables which have the wrong charset or collation, but it should prevent new databases and future additions to use the wrong set. Also made sure that the init statements are also applied during migrations, which currently did not happen. Should prevent / resolve #6611 like issues in the future. Signed-off-by: BlackDex --- .env.template | 2 +- src/db/mod.rs | 43 +++++++++++++++++++++++++++++++++++-------- 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/.env.template b/.env.template index 67f531fc..a1a27722 100644 --- a/.env.template +++ b/.env.template @@ -97,7 +97,7 @@ ## This is mainly useful for connection-scoped pragmas. ## If empty, a database-specific default is used: ## - SQLite: "PRAGMA busy_timeout = 5000; PRAGMA synchronous = NORMAL;" -## - MySQL: "" +## - MySQL: "SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci;" ## - PostgreSQL: "" # DATABASE_CONN_INIT="" diff --git a/src/db/mod.rs b/src/db/mod.rs index ae2b1221..8184f3ad 100644 --- a/src/db/mod.rs +++ b/src/db/mod.rs @@ -197,15 +197,15 @@ impl DbPool { match conn_type { #[cfg(mysql)] DbConnType::Mysql => { - mysql_migrations::run_migrations(&db_url)?; + mysql_migrations::run_migrations(&db_url, &conn_type)?; } #[cfg(postgresql)] DbConnType::Postgresql => { - postgresql_migrations::run_migrations(&db_url)?; + postgresql_migrations::run_migrations(&db_url, &conn_type)?; } #[cfg(sqlite)] DbConnType::Sqlite => { - sqlite_migrations::run_migrations(&db_url)?; + sqlite_migrations::run_migrations(&db_url, &conn_type)?; } } @@ -294,7 +294,7 @@ impl DbConnType { pub fn default_init_stmts(&self) -> String { match self { #[cfg(mysql)] - Self::Mysql => String::new(), + Self::Mysql => "SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci;".to_string(), #[cfg(postgresql)] Self::Postgresql => String::new(), #[cfg(sqlite)] @@ -460,16 +460,24 @@ impl<'r> FromRequest<'r> for DbConn { // https://docs.rs/diesel_migrations/*/diesel_migrations/macro.embed_migrations.html #[cfg(sqlite)] mod sqlite_migrations { + use super::DbConnType; use diesel::{Connection, RunQueryDsl}; use diesel_migrations::{EmbeddedMigrations, MigrationHarness}; pub const MIGRATIONS: EmbeddedMigrations = embed_migrations!("migrations/sqlite"); - pub fn run_migrations(db_url: &str) -> Result<(), super::Error> { + pub fn run_migrations(db_url: &str, conn_type: &DbConnType) -> Result<(), super::Error> { // Establish a connection to the sqlite database (this will create a new one, if it does // not exist, and exit if there is an error). let mut connection = diesel::sqlite::SqliteConnection::establish(db_url)?; // Run the migrations after successfully establishing a connection + // First run any init statement + let init_stmts = conn_type.get_init_stmts(); + if !init_stmts.is_empty() { + diesel::sql_query(init_stmts) + .execute(&mut connection) + .expect("Failed execute init statements during migrations"); + } // Disable Foreign Key Checks during migration // Scoped to a connection. diesel::sql_query("PRAGMA foreign_keys = OFF") @@ -488,14 +496,23 @@ mod sqlite_migrations { #[cfg(mysql)] mod mysql_migrations { + use super::DbConnType; use diesel::{Connection, RunQueryDsl}; use diesel_migrations::{EmbeddedMigrations, MigrationHarness}; pub const MIGRATIONS: EmbeddedMigrations = embed_migrations!("migrations/mysql"); - pub fn run_migrations(db_url: &str) -> Result<(), super::Error> { + pub fn run_migrations(db_url: &str, conn_type: &DbConnType) -> Result<(), super::Error> { // Make sure the database is up to date (create if it doesn't exist, or run the migrations) let mut connection = diesel::mysql::MysqlConnection::establish(db_url)?; + // Run the migrations after successfully establishing a connection + // First run any init statement + let init_stmts = conn_type.get_init_stmts(); + if !init_stmts.is_empty() { + diesel::sql_query(init_stmts) + .execute(&mut connection) + .expect("Failed execute init statements during migrations"); + } // Disable Foreign Key Checks during migration // Scoped to a connection/session. diesel::sql_query("SET FOREIGN_KEY_CHECKS = 0") @@ -509,14 +526,24 @@ mod mysql_migrations { #[cfg(postgresql)] mod postgresql_migrations { - use diesel::Connection; + use super::DbConnType; + use diesel::{Connection, RunQueryDsl}; use diesel_migrations::{EmbeddedMigrations, MigrationHarness}; pub const MIGRATIONS: EmbeddedMigrations = embed_migrations!("migrations/postgresql"); - pub fn run_migrations(db_url: &str) -> Result<(), super::Error> { + pub fn run_migrations(db_url: &str, conn_type: &DbConnType) -> Result<(), super::Error> { // Make sure the database is up to date (create if it doesn't exist, or run the migrations) let mut connection = diesel::pg::PgConnection::establish(db_url)?; + // Run the migrations after successfully establishing a connection + // First run any init statement + let init_stmts = conn_type.get_init_stmts(); + if !init_stmts.is_empty() { + diesel::sql_query(init_stmts) + .execute(&mut connection) + .expect("Failed execute init statements during migrations"); + } + connection.run_pending_migrations(MIGRATIONS).expect("Error running migrations"); Ok(()) }