2025-12-26 18:13:26 +01:00
from __future__ import annotations
import sys
2026-08-18 01:51:18 +02:00
from pathlib import Path
from baudolo.restore.run import docker_exec , docker_exec_sh
2025-12-26 18:13:26 +01:00
2026-08-17 05:25:29 +02:00
from .version import guard
2025-12-26 18:13:26 +01:00
2026-08-18 01:51:18 +02:00
_NO_CLIENT = "ERROR: neither 'mariadb' nor 'mysql' found in container."
2025-12-26 18:13:26 +01:00
def _pick_client ( container : str ) -> str :
2025-12-26 19:10:55 +01:00
"""
Prefer 'mariadb', fallback to 'mysql'.
Some MariaDB images no longer ship a 'mysql' binary, so we must not assume it exists.
"""
2025-12-26 18:13:26 +01:00
script = r """
set -eu
if command -v mariadb >/dev/null 2>&1; then echo mariadb; exit 0; fi
if command -v mysql >/dev/null 2>&1; then echo mysql; exit 0; fi
exit 42
"""
try :
out = docker_exec_sh ( container , script , capture = True ) . stdout . decode () . strip ()
2026-08-17 04:35:50 +02:00
except Exception :
2026-08-18 01:51:18 +02:00
print ( _NO_CLIENT , file = sys . stderr )
2026-08-17 04:35:50 +02:00
raise
2026-08-18 01:51:18 +02:00
if not out :
print ( _NO_CLIENT , file = sys . stderr )
raise RuntimeError ( "empty client detection output" )
return out
2025-12-26 18:13:26 +01:00
def restore_mariadb_sql (
* ,
container : str ,
db_name : str ,
user : str ,
password : str ,
sql_path : str ,
empty : bool ,
2026-08-17 05:25:29 +02:00
check_version : bool = True ,
2025-12-26 18:13:26 +01:00
) -> None :
client = _pick_client ( container )
2026-08-18 01:51:18 +02:00
if not Path ( sql_path ) . is_file ():
2025-12-26 18:13:26 +01:00
raise FileNotFoundError ( sql_path )
2026-08-17 05:25:29 +02:00
if check_version :
guard (
sql_path = sql_path ,
engine = "mariadb" ,
container = container ,
user = user ,
password = password ,
client = client ,
)
2025-12-26 18:13:26 +01:00
if empty :
2026-08-17 04:35:50 +02:00
# Do not hardcode 'mysql': MariaDB 11 images may not ship that binary.
2025-12-26 18:13:26 +01:00
result = docker_exec (
container ,
[
2025-12-26 19:10:55 +01:00
client ,
2025-12-26 18:13:26 +01:00
"-u" ,
user ,
f "--password= { password } " ,
"-N" ,
"-e" ,
2026-08-18 01:51:18 +02:00
f "SELECT table_name FROM information_schema.tables WHERE table_schema = ' { db_name } ';" , # noqa: S608 - validate_database() constrains the name to ^[a-zA-Z0-9_][a-zA-Z0-9_-]*$
2025-12-26 18:13:26 +01:00
],
capture = True ,
)
tables = result . stdout . decode () . split ()
2025-12-26 19:10:55 +01:00
2026-07-11 08:23:11 +02:00
if tables :
# SET FOREIGN_KEY_CHECKS is session-scoped, so it must share one
# client session with the DROPs or FK constraints still fire.
drop_sql = (
"SET FOREIGN_KEY_CHECKS=0; "
+ " " . join (
f "DROP TABLE IF EXISTS ` { db_name } `.` { tbl } `;" for tbl in tables
)
+ " SET FOREIGN_KEY_CHECKS=1;"
)
2025-12-26 18:13:26 +01:00
docker_exec (
container ,
[
2025-12-26 19:10:55 +01:00
client ,
2025-12-26 18:13:26 +01:00
"-u" ,
user ,
f "--password= { password } " ,
"-e" ,
2026-07-11 08:23:11 +02:00
drop_sql ,
2025-12-26 18:13:26 +01:00
],
)
2026-08-18 01:51:18 +02:00
with Path ( sql_path ) . open ( "rb" ) as f :
2025-12-28 22:12:31 +01:00
docker_exec (
container , [ client , "-u" , user , f "--password= { password } " , db_name ], stdin = f
)
2025-12-26 18:13:26 +01:00
print ( f "MariaDB/MySQL restore complete for db ' { db_name } '." )