Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
|
@@ -154,19 +154,21 @@ def import_sql_dump_to_sqlite(db_path: str, sql_text: str) -> None:
|
|
| 154 |
"""
|
| 155 |
Convertidor avanzado MySQL → SQLite.
|
| 156 |
Limpia, reordena y ejecuta el schema de forma segura en SQLite.
|
|
|
|
| 157 |
"""
|
| 158 |
|
| 159 |
# ======================================================
|
| 160 |
# 1) Limpieza inicial del dump
|
| 161 |
# ======================================================
|
| 162 |
|
| 163 |
-
# Remover comentarios estilo MySQL
|
| 164 |
sql_text = re.sub(r"/\*![\s\S]*?\*/;", "", sql_text)
|
| 165 |
sql_text = re.sub(r"/\*[\s\S]*?\*/", "", sql_text)
|
| 166 |
sql_text = re.sub(r"--.*?\n", "", sql_text)
|
| 167 |
|
| 168 |
-
# Remover
|
| 169 |
sql_text = re.sub(r"DELIMITER\s+.+", "", sql_text)
|
|
|
|
| 170 |
|
| 171 |
# Quitar ENGINE, ROW_FORMAT, AUTO_INCREMENT
|
| 172 |
sql_text = re.sub(r"ENGINE=\w+", "", sql_text)
|
|
@@ -178,7 +180,11 @@ def import_sql_dump_to_sqlite(db_path: str, sql_text: str) -> None:
|
|
| 178 |
sql_text = re.sub(r"CHARACTER SET \w+", "", sql_text)
|
| 179 |
sql_text = re.sub(r"COLLATE \w+", "", sql_text)
|
| 180 |
|
| 181 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
| 182 |
sql_text = sql_text.replace("`", "")
|
| 183 |
|
| 184 |
# ======================================================
|
|
@@ -279,8 +285,21 @@ def import_sql_dump_to_sqlite(db_path: str, sql_text: str) -> None:
|
|
| 279 |
|
| 280 |
cur.execute("PRAGMA foreign_keys = ON;")
|
| 281 |
conn.commit()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 282 |
conn.close()
|
| 283 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 284 |
|
| 285 |
def extract_table_name(create_stmt: str) -> str:
|
| 286 |
m = re.search(r"CREATE TABLE\s+(\w+)", create_stmt, re.IGNORECASE)
|
|
@@ -754,7 +773,7 @@ async def upload_database(db_file: UploadFile = File(...)):
|
|
| 754 |
Subida universal de BD.
|
| 755 |
El usuario puede subir:
|
| 756 |
- .sqlite / .db → se usa tal cual
|
| 757 |
-
- .sql → dump MySQL/
|
| 758 |
- .csv → se crea una BD SQLite y una tabla
|
| 759 |
- .zip → puede contener .sqlite/.db, .sql o .csv (se detecta automáticamente)
|
| 760 |
"""
|
|
@@ -782,8 +801,28 @@ async def upload_database(db_file: UploadFile = File(...)):
|
|
| 782 |
conn_id = create_empty_sqlite_db(label=filename)
|
| 783 |
db_path = DB_REGISTRY[conn_id]["db_path"]
|
| 784 |
sql_text = contents.decode("utf-8", errors="ignore")
|
| 785 |
-
|
| 786 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 787 |
|
| 788 |
# Caso 3: CSV simple
|
| 789 |
elif fname_lower.endswith(".csv"):
|
|
@@ -832,8 +871,27 @@ async def upload_database(db_file: UploadFile = File(...)):
|
|
| 832 |
parts.append(f.read().decode("utf-8", errors="ignore"))
|
| 833 |
sql_text = "\n\n".join(parts)
|
| 834 |
|
| 835 |
-
|
| 836 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 837 |
|
| 838 |
# 4.3: solo CSVs
|
| 839 |
elif csv_names:
|
|
|
|
| 154 |
"""
|
| 155 |
Convertidor avanzado MySQL → SQLite.
|
| 156 |
Limpia, reordena y ejecuta el schema de forma segura en SQLite.
|
| 157 |
+
Si al final no se crea ninguna tabla, lanza ValueError.
|
| 158 |
"""
|
| 159 |
|
| 160 |
# ======================================================
|
| 161 |
# 1) Limpieza inicial del dump
|
| 162 |
# ======================================================
|
| 163 |
|
| 164 |
+
# Remover comentarios estilo MySQL/PostgreSQL
|
| 165 |
sql_text = re.sub(r"/\*![\s\S]*?\*/;", "", sql_text)
|
| 166 |
sql_text = re.sub(r"/\*[\s\S]*?\*/", "", sql_text)
|
| 167 |
sql_text = re.sub(r"--.*?\n", "", sql_text)
|
| 168 |
|
| 169 |
+
# Remover DELIMITER y statements SET típicos
|
| 170 |
sql_text = re.sub(r"DELIMITER\s+.+", "", sql_text)
|
| 171 |
+
sql_text = re.sub(r"SET\s+[^;]+;", "", sql_text)
|
| 172 |
|
| 173 |
# Quitar ENGINE, ROW_FORMAT, AUTO_INCREMENT
|
| 174 |
sql_text = re.sub(r"ENGINE=\w+", "", sql_text)
|
|
|
|
| 180 |
sql_text = re.sub(r"CHARACTER SET \w+", "", sql_text)
|
| 181 |
sql_text = re.sub(r"COLLATE \w+", "", sql_text)
|
| 182 |
|
| 183 |
+
# Quitar dominios y tipos personalizados (PostgreSQL)
|
| 184 |
+
sql_text = re.sub(r"CREATE\s+DOMAIN[\s\S]+?;", "", sql_text, flags=re.IGNORECASE)
|
| 185 |
+
sql_text = re.sub(r"CREATE\s+TYPE[\s\S]+?;", "", sql_text, flags=re.IGNORECASE)
|
| 186 |
+
|
| 187 |
+
# Reemplazar backticks por nada (MySQL → SQLite)
|
| 188 |
sql_text = sql_text.replace("`", "")
|
| 189 |
|
| 190 |
# ======================================================
|
|
|
|
| 285 |
|
| 286 |
cur.execute("PRAGMA foreign_keys = ON;")
|
| 287 |
conn.commit()
|
| 288 |
+
|
| 289 |
+
# ======================================================
|
| 290 |
+
# 6) Validar que realmente se crearon tablas
|
| 291 |
+
# ======================================================
|
| 292 |
+
cur.execute("SELECT COUNT(*) FROM sqlite_master WHERE type='table';")
|
| 293 |
+
n_tables = cur.fetchone()[0]
|
| 294 |
conn.close()
|
| 295 |
|
| 296 |
+
if n_tables == 0:
|
| 297 |
+
raise ValueError(
|
| 298 |
+
"No se creó ninguna tabla desde el dump SQL. "
|
| 299 |
+
"Probablemente es un dump de otro motor (por ejemplo PostgreSQL) "
|
| 300 |
+
"o usa sintaxis no compatible con SQLite."
|
| 301 |
+
)
|
| 302 |
+
|
| 303 |
|
| 304 |
def extract_table_name(create_stmt: str) -> str:
|
| 305 |
m = re.search(r"CREATE TABLE\s+(\w+)", create_stmt, re.IGNORECASE)
|
|
|
|
| 773 |
Subida universal de BD.
|
| 774 |
El usuario puede subir:
|
| 775 |
- .sqlite / .db → se usa tal cual
|
| 776 |
+
- .sql → dump MySQL/SQLite (best effort → convertido a SQLite)
|
| 777 |
- .csv → se crea una BD SQLite y una tabla
|
| 778 |
- .zip → puede contener .sqlite/.db, .sql o .csv (se detecta automáticamente)
|
| 779 |
"""
|
|
|
|
| 801 |
conn_id = create_empty_sqlite_db(label=filename)
|
| 802 |
db_path = DB_REGISTRY[conn_id]["db_path"]
|
| 803 |
sql_text = contents.decode("utf-8", errors="ignore")
|
| 804 |
+
|
| 805 |
+
try:
|
| 806 |
+
import_sql_dump_to_sqlite(db_path, sql_text)
|
| 807 |
+
note = "SQL dump imported into SQLite (best effort)."
|
| 808 |
+
except Exception as e:
|
| 809 |
+
# Limpiar registro porque la BD quedó inútil
|
| 810 |
+
try:
|
| 811 |
+
if os.path.exists(db_path):
|
| 812 |
+
os.remove(db_path)
|
| 813 |
+
except Exception:
|
| 814 |
+
pass
|
| 815 |
+
DB_REGISTRY.pop(conn_id, None)
|
| 816 |
+
|
| 817 |
+
raise HTTPException(
|
| 818 |
+
status_code=400,
|
| 819 |
+
detail=(
|
| 820 |
+
"No se pudo convertir este archivo .sql a una base de datos SQLite utilizable. "
|
| 821 |
+
"Es probable que sea un dump de otro motor (por ejemplo PostgreSQL) "
|
| 822 |
+
"o que contenga sintaxis avanzada no compatible con SQLite.\n"
|
| 823 |
+
f"Detalle técnico: {e}"
|
| 824 |
+
),
|
| 825 |
+
)
|
| 826 |
|
| 827 |
# Caso 3: CSV simple
|
| 828 |
elif fname_lower.endswith(".csv"):
|
|
|
|
| 871 |
parts.append(f.read().decode("utf-8", errors="ignore"))
|
| 872 |
sql_text = "\n\n".join(parts)
|
| 873 |
|
| 874 |
+
try:
|
| 875 |
+
import_sql_dump_to_sqlite(db_path, sql_text)
|
| 876 |
+
note = "SQL dump(s) from ZIP imported into SQLite."
|
| 877 |
+
except Exception as e:
|
| 878 |
+
try:
|
| 879 |
+
if os.path.exists(db_path):
|
| 880 |
+
os.remove(db_path)
|
| 881 |
+
except Exception:
|
| 882 |
+
pass
|
| 883 |
+
DB_REGISTRY.pop(conn_id, None)
|
| 884 |
+
|
| 885 |
+
raise HTTPException(
|
| 886 |
+
status_code=400,
|
| 887 |
+
detail=(
|
| 888 |
+
"No se pudo convertir los archivos .sql dentro del ZIP "
|
| 889 |
+
"a una base de datos SQLite utilizable. "
|
| 890 |
+
"Es probable que sean dumps de otro motor (por ejemplo PostgreSQL) "
|
| 891 |
+
"o que contengan sintaxis avanzada no compatible con SQLite.\n"
|
| 892 |
+
f"Detalle técnico: {e}"
|
| 893 |
+
),
|
| 894 |
+
)
|
| 895 |
|
| 896 |
# 4.3: solo CSVs
|
| 897 |
elif csv_names:
|