"""Patch setup_db.py to import SQL via Python instead of mariadb CLI. The `mariadb` CLI restore fails with some external MySQL hosts. This patch replaces the CLI-based restore with direct `frappe.db.sql_ddl()` calls, reading and executing the SQL file through the existing Python connection. """ import os, re SETUP_DB = "apps/frappe/frappe/database/mariadb/setup_db.py" if not os.path.exists(SETUP_DB): print(f"ERROR: {SETUP_DB} not found") exit(1) with open(SETUP_DB) as f: content = f.read() OLD_CALL = '''\tDbManager(frappe.local.db).restore_database( \t\tverbose, db_name, source_sql, db_name, frappe.conf.db_password \t)''' NEW_CALL = '''\t# PATCHED: Import SQL via Python connection (avoids mariadb CLI issues) \tprint(f" [patch] Reading SQL from {source_sql}") \twith open(source_sql, "r", encoding="utf-8") as _f: \t\t_raw = _f.read() \t# Split on ";\\n" to isolate individual SQL statements. \t# Each fragment may contain leading comment lines ("-- ...") — strip them \t# so that CREATE / DROP / INSERT statements are not silently skipped. \t_clean_statements = [] \tfor _frag in _raw.replace("\\r\\n", "\\n").split(";\\n"): \t\t_lines = [] \t\tfor _line in _frag.split("\\n"): \t\t\t_stripped = _line.strip() \t\t\tif _stripped.startswith("--") or _stripped == "": \t\t\t\tcontinue \t\t\t_lines.append(_line) \t\t_cleaned = "\\n".join(_lines).strip() \t\tif _cleaned and _cleaned != ";": \t\t\t_clean_statements.append(_cleaned) \tprint(f" [patch] Found {len(_clean_statements)} SQL statements, importing via db.sql_ddl...") \t_ok = _fail = 0 \tfor _idx, _stmt in enumerate(_clean_statements): \t\ttry: \t\t\tfrappe.db.sql_ddl(_stmt) \t\t\t_ok += 1 \t\texcept Exception as _e: \t\t\t_fail += 1 \t\t\tprint(f" [patch] STMT {_idx} FAIL: {_stmt[:160]}... ({_e})") \t\t\tpass \tprint(f" [patch] SQL import complete: {_ok} OK, {_fail} failed") \tprint(f" [patch] Tables after import: {frappe.db.get_tables(cached=False)}")''' PATCHES = 0 if OLD_CALL in content: content = content.replace(OLD_CALL, NEW_CALL) PATCHES += 1 print(f"SUCCESS: Patched restore_database call -> Python SQL import") else: print(f"WARNING: Could not find restore_database call in {SETUP_DB}") if 'import_db_from_sql' in content: idx = content.index('import_db_from_sql') print("--- Around import_db_from_sql ---") print(content[idx:idx+600]) # Also patch the tabDefaultValue table check to be case-insensitive # External MySQL may have lower_case_table_names=1, returning lowercase table names OLD_CHECK = '''\tif "tabDefaultValue" not in frappe.db.get_tables(cached=False):''' NEW_CHECK = '''\t_tables_lower = [t.lower() for t in frappe.db.get_tables(cached=False)] \tif "tabdefaultvalue" not in _tables_lower:''' if OLD_CHECK in content: content = content.replace(OLD_CHECK, NEW_CHECK) PATCHES += 1 print(f"SUCCESS: Patched tabDefaultValue check -> case-insensitive") else: print(f"WARNING: Could not find tabDefaultValue check in {SETUP_DB}") if 'tabDefaultValue' in content: idx = content.index('tabDefaultValue') print(f"--- Around tabDefaultValue (offset {idx}) ---") print(content[max(0,idx-60):idx+200]) if PATCHES: with open(SETUP_DB, 'w') as f: f.write(content) print(f"DONE: Applied {PATCHES} patch(es) to {SETUP_DB}") else: print(f"FAILED: No patches applied")