sábado, 4 de octubre de 2025

Disco NTFS con problemas de lectura.Multiples ficheros duplicados.Copiar sin duplicados

 1. Detectar el problema

En la herramienta "disks" de Ubuntu me dice:

s'ha produit un error en muntar el sistema de fitxers Error mounting /dev/sdc4 at /media/eduard/22143F41143F176F;wrong fs type, bad option, bad superblock on /dev/sdc4, missing codepage or helper program, or other error (udisks-error-quark,0)

Por tanto el problema es en el disco /dev/sdc4


2. Detectar el tipo de partición

Ejecutamos:

sudo fdisk -l

Y vemos que es NTFS (que también se podía ver en la herramienta "Disks" de ubuntu

3. Probar a montar manualmente

Ejecutamos:

sudo mount -t ntfs /dev/sdc4 /mnt

y responde:

$MFTMirr does not match $MFT (record 3). Failed to mount '/dev/sdc4': Error d’Entrada/Sortida NTFS is either inconsistent, or there is a hardware fault, or it's a SoftRAID/FakeRAID hardware. In the first case run chkdsk /f on Windows then reboot into Windows twice. The usage of the /f parameter is very important! If the device is a SoftRAID/FakeRAID then first activate it and mount a different device under the /dev/mapper/ directory, (e.g. /dev/mapper/nvidia_eahaabcc1). Please see the 'dmraid' documentation for more details.

4. Instalar software adicional y utilizarlo


sudo apt install ntfs-3g

sudo ntfsfix /dev/sdc4

y contesta:
Mounting volume... Windows is hibernated, refused to mount. FAILED Attempting to correct errors... Processing $MFT and $MFTMirr... Reading $MFT... OK Reading $MFTMirr... OK Comparing $MFTMirr to $MFT... OK Processing of $MFT and $MFTMirr completed successfully. Setting required flags on partition... OK Going to empty the journal ($LogFile)... OK Windows is hibernated, refused to mount. Remount failed: Operation not permitted

cosa que dice que windows está "hibernated" y se debe a que se interrumpioun proceso de E/AS en el disco y que no se debe acceder a el para modificar nada si no se opera desde windows.

Por tanto lo que podemos hacer es montarlo de solo lectura y copiar los datos 

4. Montar el disco de solo lectura


sudo mount -t ntfs-3g -o ro /dev/sdc4 /mnt

y ya podemos acceder a el disco o la partición en el directorio /mnt

5. Copia de seguridad multifichero


Se opta por copiar a otro disco mas grande y luego crear un zip multivolumen de ficheros de 4 gigas con:

cd /home/eduard
zip -r -s 4g MIS_DATOS.zip MIS_DATOS

donde la carpeta que contiene los datos a guradar es /home/eduard/MIS_DATOS
 y esto genera estos ficheros:

MIS_DATOS.z01, MIS_DATOS.z02, ....MIS_DATOS.88, MIS_DATOS.zip

Siendo MIS_DATOS.88 en mi caso el penúltimo fichero y MIS_DATOS.zip el último

Para descomprimir utilizar:

unzip MIS_DATOS.zip

asegurándose que todos los ficheros generados esten disponibles en la misma carpeta.

Si solo se quiere extraer una subcarpeta llamada MI_SUBCARPETA se haría_

unzip MIS_DATOS.zip "MIS_DATOS/MI_SUBCARPETA/*" -d /ruta/de/destino

y si solo queremos listar el contenido:

unzip -l MIS_DATOS.zip | less


6. Ver si hay muchos duplicados. Borrado y copia sin duplicados


Con chatgpt he creado un bash para detectar duplicados y en su caso borrarlos. Pero en el siguiente punto se ha hecho un bash que solo copia los ficheros no duplicados.

El fichero a no borrar de los duplicados es aquel que tiene menor numero de anidamientos en carpeta.

Ver y/o eliminar duplicados (buscar_duplicados_carpeta.sh)

#!/bin/bash
# buscar_duplicados.sh
# Busca y (opcionalmente) elimina ficheros duplicados (por nombre y tamaño)
# e informa del espacio total y del potencial ahorro.

# Uso:
#   ./buscar_duplicados.sh /ruta/a/carpeta [eliminar_duplicados]

BASE_DIR="$1"
ACCION="$2"

# --- Validaciones iniciales
if [[ -z "$BASE_DIR" ]]; then
  echo "❌ Uso: $0 /ruta/a/carpeta [eliminar_duplicados]"
  exit 1
fi

if [[ ! -d "$BASE_DIR" ]]; then
  echo "❌ No existe el directorio: $BASE_DIR"
  exit 1
fi

# --- Variables
REPORT_FILE="duplicados_$(date +%Y%m%d_%H%M%S).txt"
> "$REPORT_FILE"
ELIMINAR=false
if [[ "$ACCION" == "eliminar_duplicados" ]]; then
  ELIMINAR=true
fi

echo "🔍 Buscando duplicados en: $BASE_DIR"
echo "📝 Informe: $REPORT_FILE"
echo

# --- Calcular espacio total de la carpeta (en bytes)
TOTAL_BYTES=$(du -sb "$BASE_DIR" | awk '{print $1}')
TOTAL_HUMAN=$(du -sh "$BASE_DIR" | awk '{print $1}')

echo "💾 Espacio total actual ocupado por la carpeta: $TOTAL_HUMAN ($TOTAL_BYTES bytes)"
echo "💾 Espacio total actual ocupado por la carpeta: $TOTAL_HUMAN ($TOTAL_BYTES bytes)" >> "$REPORT_FILE"
echo

# --- Buscar duplicados
POTENCIAL_AHORRO=0

find "$BASE_DIR" -type f -printf '%s\t%p\n' | \
while IFS=$'\t' read -r SIZE FILE; do
    BASENAME=$(basename "$FILE")
    echo -e "$BASENAME\t$SIZE\t$FILE"
done | sort -k1,1 -k2,2n | \
awk -F'\t' -v ELIMINAR="$ELIMINAR" -v REPORT_FILE="$REPORT_FILE" -v BASE_DIR="$BASE_DIR" '
function human(bytes) {
    hum[1024^4]="TB"; hum[1024^3]="GB"; hum[1024^2]="MB"; hum[1024]="KB";
    for (x=1024^4; x>=1024; x/=1024) if (bytes>=x) return sprintf("%.2f %s", bytes/x, hum[x]);
    return bytes " B";
}

{
    name=$1; size=$2; path=$3;
    key=name"|"size;
    files[key]=files[key]?files[key]"\n"path:path;
    sizes[key]=size;
}
END {
    total_savings=0;
    for (k in files) {
        split(files[k], arr, "\n");
        if (length(arr) > 1) {
            print "📁 Duplicado: " k >> REPORT_FILE;
            print "📁 Duplicado: " k;

            # Calcular profundidad
            min=999999; minpath="";
            for (i in arr) {
                split(arr[i], dirs, "/");
                depth[i]=length(dirs);
                if (depth[i] < min) {
                    min=depth[i];
                    minpath=arr[i];
                }
            }

            print "   ✅ Se conservará: " minpath >> REPORT_FILE;
            print "   ✅ Se conservará: " minpath;

            print "   ❌ Duplicados encontrados:" >> REPORT_FILE;
            for (i in arr) {
                if (arr[i] != minpath && arr[i] != "") {
                    print "      " arr[i] >> REPORT_FILE;
                    print "      " arr[i];
                    total_savings += sizes[k];  # sumamos el tamaño de los que se podrían eliminar
                }
            }

            if (ELIMINAR == "true") {
                printf "\n¿Eliminar todos los duplicados de este grupo (excepto el conservado)? (s/n): ";
                getline resp < "/dev/tty";
                if (resp == "s" || resp == "S") {
                    for (i in arr) {
                        if (arr[i] != minpath && arr[i] != "") {
                            cmd="rm -i \""arr[i]"\"";
                            system(cmd);
                            print "      ➜ Eliminado: " arr[i] >> REPORT_FILE;
                        }
                    }
                } else {
                    print "      ➜ No se eliminaron duplicados de este grupo." >> REPORT_FILE;
                }
            }
            print "" >> REPORT_FILE;
            print "";
        }
    }
    print "--------------------------------------------" >> REPORT_FILE;
    print "📊 Espacio total potencial a liberar: " human(total_savings) " (" total_savings " bytes)" >> REPORT_FILE;
    print "--------------------------------------------" >> REPORT_FILE;

    print "\n📊 Espacio total potencial a liberar: " human(total_savings) " (" total_savings " bytes)";
}' 

echo
echo "✅ Proceso finalizado."
echo "📄 Informe generado en: $(realpath "$REPORT_FILE")"

# --- Calcular de nuevo el tamaño si se eliminaron duplicados
if [[ "$ELIMINAR" == "true" ]]; then
  FINAL_BYTES=$(du -sb "$BASE_DIR" | awk '{print $1}')
  FINAL_HUMAN=$(du -sh "$BASE_DIR" | awk '{print $1}')
  echo
  echo "💾 Espacio tras eliminar duplicados: $FINAL_HUMAN ($FINAL_BYTES bytes)"
  echo "💾 Espacio tras eliminar duplicados: $FINAL_HUMAN ($FINAL_BYTES bytes)" >> "$REPORT_FILE"
fi

copiar carpeta sin duplicados creando un fichero de duplicados dentro de la carpeta destino (copiar_carpeta_no_duplicados.sh)


#!/bin/bash
# copiar_sin_duplicados.sh
# Copia solo los ficheros no duplicados (por nombre y tamaño)
# y genera informes de los archivos copiados y duplicados.

echo "📁 Introduce la ruta de la carpeta origen:"
read -r ORIGEN

if [[ ! -d "$ORIGEN" ]]; then
  echo "❌ La carpeta origen no existe."
  exit 1
fi

echo "📂 Introduce la ruta de la carpeta destino:"
read -r DESTINO

# Crear carpeta destino si no existe
mkdir -p "$DESTINO"

# Archivos temporales e informes
TMP_FILE=$(mktemp)
REPORT_FILE="copiados_$(date +%Y%m%d_%H%M%S).txt"
DUPLICADOS_FILE="$DESTINO/duplicados_$(date +%Y%m%d_%H%M%S).txt"
> "$REPORT_FILE"
> "$DUPLICADOS_FILE"

echo "🔍 Analizando duplicados en: $ORIGEN"
echo

# --- Calcular tamaño total de la carpeta origen
TOTAL_BYTES=$(du -sb "$ORIGEN" | awk '{print $1}')
TOTAL_HUMAN=$(du -sh "$ORIGEN" | awk '{print $1}')
echo "💾 Tamaño total de la carpeta origen: $TOTAL_HUMAN ($TOTAL_BYTES bytes)"
echo "💾 Tamaño total de la carpeta origen: $TOTAL_HUMAN ($TOTAL_BYTES bytes)" >> "$REPORT_FILE"
echo >> "$REPORT_FILE"

# --- Buscar duplicados (por nombre + tamaño)
find "$ORIGEN" -type f -printf '%s\t%p\n' | \
while IFS=$'\t' read -r SIZE FILE; do
    BASENAME=$(basename "$FILE")
    echo -e "$BASENAME\t$SIZE\t$FILE"
done | sort -k1,1 -k2,2n | \
awk -F'\t' -v TMP_FILE="$TMP_FILE" -v DUPLICADOS_FILE="$DUPLICADOS_FILE" '
{
    name=$1; size=$2; path=$3;
    key=name"|"size;
    files[key]=files[key]?files[key]"\n"path:path;
    sizes[key]=size;
}
END {
    for (k in files) {
        split(files[k], arr, "\n");
        if (length(arr) > 1) {
            print "-----------------------------------" >> DUPLICADOS_FILE;
            print "Duplicado (" k "):" >> DUPLICADOS_FILE;
            for (i in arr) {
                print arr[i] >> TMP_FILE;
                print "   " arr[i] >> DUPLICADOS_FILE;
            }
        }
    }
}'

# --- Copiar solo los no duplicados
echo "🚀 Iniciando copia de ficheros no duplicados..."
echo >> "$REPORT_FILE"
echo "=============================" >> "$REPORT_FILE"
echo "COPIA DE FICHEROS NO DUPLICADOS" >> "$REPORT_FILE"
echo "=============================" >> "$REPORT_FILE"
echo >> "$REPORT_FILE"

COUNT=0
BYTES_TOTAL_COPIADOS=0
BYTES_DUPLICADOS=0

# Calcular espacio potencial de duplicados
while IFS= read -r FILE_DUP; do
    if [[ -f "$FILE_DUP" ]]; then
        SIZE=$(stat -c%s "$FILE_DUP")
        BYTES_DUPLICADOS=$((BYTES_DUPLICADOS + SIZE))
    fi
done < "$TMP_FILE"

# Copiar los no duplicados
while IFS= read -r FILE; do
    # Saltar duplicados
    if grep -Fxq "$FILE" "$TMP_FILE"; then
        continue
    fi

    REL_PATH="${FILE#$ORIGEN/}"
    DEST_PATH="$DESTINO/$REL_PATH"
    DEST_DIR=$(dirname "$DEST_PATH")

    mkdir -p "$DEST_DIR"
    cp -p "$FILE" "$DEST_PATH"

    SIZE=$(stat -c%s "$FILE")
    BYTES_TOTAL_COPIADOS=$((BYTES_TOTAL_COPIADOS + SIZE))
    COUNT=$((COUNT + 1))

    echo "✔ Copiado: $DEST_PATH" >> "$REPORT_FILE"
done < <(find "$ORIGEN" -type f)

# --- Función para tamaños legibles
function human_readable {
    local bytes=$1
    local units=("B" "KB" "MB" "GB" "TB")
    local i=0
    local value=$bytes
    while (( value >= 1024 && i < 4 )); do
        value=$(( value / 1024 ))
        ((i++))
    done
    echo "${value} ${units[$i]}"
}

TOTAL_HUMAN_COPIADOS=$(human_readable "$BYTES_TOTAL_COPIADOS")
TOTAL_HUMAN_DUPLICADOS=$(human_readable "$BYTES_DUPLICADOS")

# --- Resultados finales
echo
echo "✅ Copia completada."
echo "📊 Archivos copiados: $COUNT"
echo "📦 Tamaño total origen: $TOTAL_HUMAN ($TOTAL_BYTES bytes)"
echo "📥 Tamaño copiado (sin duplicados): $TOTAL_HUMAN_COPIADOS ($BYTES_TOTAL_COPIADOS bytes)"
echo "💾 Espacio ahorrado (duplicados no copiados): $TOTAL_HUMAN_DUPLICADOS ($BYTES_DUPLICADOS bytes)"
echo
echo "📄 Informe de copiados: $(realpath "$REPORT_FILE")"
echo "📁 Listado de duplicados: $(realpath "$DUPLICADOS_FILE")"

echo >> "$REPORT_FILE"
echo "-------------------------------------" >> "$REPORT_FILE"
echo "📦 Tamaño total origen: $TOTAL_HUMAN ($TOTAL_BYTES bytes)" >> "$REPORT_FILE"
echo "📥 Tamaño copiado (sin duplicados): $TOTAL_HUMAN_COPIADOS ($BYTES_TOTAL_COPIADOS bytes)" >> "$REPORT_FILE"
echo "💾 Espacio ahorrado (duplicados no copiados): $TOTAL_HUMAN_DUPLICADOS ($BYTES_DUPLICADOS bytes)" >> "$REPORT_FILE"
echo "-------------------------------------" >> "$REPORT_FILE"

# --- Limpieza
rm -f "$TMP_FILE"




viernes, 3 de octubre de 2025

WEBPROPv2 (X). Solución de errores

 1. [Errno 98] error while attempting to bind on address ('192.168.28.16', 5000): address already in use

Que sudede al ejecutar softprop/menus/mnu_main.py en la máquina remota

Ejecutamos:

lsof -i :5000

Y nos da el proceso:

COMMAND     PID        USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
xmopenres 97139 informatica   13u  IPv4 741536      0t0  TCP SRV-osTicket-CAU:5000 (LISTEN)

Ahora hacemos un kill

kill -9 id_del_proceso


WEBPROPv2 (IX). Postgres devuelve resultados distintos en función de la máquina cliente

 Casi me vuelvo loco veamos lo que sucedía:

  1. Los mismos parámetros de conexion (url,usuario, contraseña,base de datos, puerto) se usan en 2 máquinas clientes distintas
  2. Cada máquina encontraba alguna base de datos que no veía la otra
  3. Para una base de datos comun (openweb), una máquina veía un esquema que la otra no veía
  4. Cuando listabas las BD con sus parámetros, se veía que el tamaño era distinto.
Plantamiento de solución:
  1. Hago una copia de seguridad con pgadmin de la BD buena
  2. La versión de postgres del servidor era la 12. Instalo la 14 en la misma máquina.
  3. Con la sentencia pg_lsclusters listo los clusters qye hay y me muestra 2 clusters, uno de la versión 12 y otro de la 14
  4. Borramos el cluster de la 14 con sudo pg_dropcluster 16 main --stop (para poder impoirtar ahí el de la 12) 
  5. Realizamos la migración del cluster 12 al 14 con sudo pg_upgradecluster 12 main 
  6. Paramos el cluster de la 12 con sudo pg_dropcluster 12 main y quitamos los servicios de la 12
  7. TODAVIA DABA RESULTADOS DIFERENTES.
  8. Compruebo con psql en ambos lados "SELECT version();" y en un cliente me devuelve la versión 12 y en otro la versión 14
  9. Desisnstalo la versión de postgres 12 del servidor con 
    sudo apt remove postgresql-12 postgresql-client-12 
  10. Rearranco la máquina y a funcionar!!!!
Adjunto el fichero python xmdbv4.py donde al final aparecen comprobaciones por si acaso sucede algo parecido:

from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
from sqlalchemy.orm import sessionmaker, declarative_base
from sqlalchemy import inspect, select, text

from typing import Any, AsyncGenerator, Awaitable
from contextlib import asynccontextmanager
from pathlib import Path
import os, sys
import asyncio

#------ Import from other folders (basicutils)
# --- Ensure project root is in sys.path when run directly ---
PROJECT_ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
if PROJECT_ROOT not in sys.path:
    sys.path.insert(0, PROJECT_ROOT)
from softprop.basicutils import xmcrypto
from softprop import xmproject_folder  # <-- Add this import
# ------End imprescindible

# --- Load properties
#mydb_conf_file = os.getenv("CONF_PYTHON") + "db.encrypted.yml"
mydb_conf_file = xmproject_folder.get_project_static_conf_params_encr_folder() + os.sep + "db.encrypted.yml"
mydb_all_props = xmcrypto.get_properties_from_file(mydb_conf_file)
my_main_db = mydb_all_props["actualDBs"][0]
is_async = True

# -- Filter only active DBs
mydb_props = {
    k: mydb_all_props[k] for k in mydb_all_props["actualDBs"] if k in mydb_all_props
}
myschema = {k: mydb_props[k]["dbSchema"] for k in mydb_props if "dbSchema" in mydb_props[k]}


def get_db_props(key=None) -> dict:
    """Build connection parameters"""
    props = mydb_props[key].copy()

    if is_async:
        props["url"] = (
            f"{props['dbType']}://{props['dbUser']}:{props['dbPwd']}"
            f"@{props['dbHost']}:{str(props['dbPort'])}/{props['dbName']}"
        )
        if props.get("appName"):
            props["connect_args"] = {
                "server_settings": {"application_name": props["appName"]}
            }
    else:
        app_url = f"?application_name={props['appName']}" if props.get("appName") else ""
        props["url"] = (
            f"{props['dbType']}://{props['dbUser']}:{props['dbPwd']}"
            f"@{props['dbHost']}:{str(props['dbPort'])}/{props['dbName']}" + app_url
        )

    my_props = {
        k: props[k]
        for k in {
            "url",
            "pool_size",
            "max_overflow",
            "pool_timeout",
            "pool_recycle",
            "pool_pre_ping",
            "connect_args",
            "echo",
            "future",
            "poolclass",
            "execution_options",
            "json_serializer",
        }
        if k in props
    }
    return my_props


# Singleton engines + sessionmakers (lazy init)
async_engines = {}
async_sessionmakers = {}


def get_engine(db_key: str):
    """Lazily create engine + sessionmaker for a DB key."""
    if db_key not in async_engines:
        config = get_db_props(db_key)

        engine = create_async_engine(
            config["url"],
            **{k: config[k] for k in config if k != "url"},
        )
        async_engines[db_key] = engine
        async_sessionmakers[db_key] = sessionmaker(
            bind=engine, class_=AsyncSession, expire_on_commit=False
        )
    return async_engines[db_key]


# Base class for ORM models
Base = declarative_base()


# ─── Async Session Generator ───────────────────────────────────────────────
@asynccontextmanager
async def get_async_session(db_key: str) -> AsyncGenerator[AsyncSession, None]:
    """Usage: async with get_async_session(db_key) as session:"""
    get_engine(db_key)  # ensure engine exists in current loop
    async with async_sessionmakers[db_key]() as session:
        yield session

# ─── Create all tables for a given DB key ──────────────────────────────────
async def create_all_tables(db_key: str):
	"""Create all tables for the given DB key."""
	engine = get_engine(db_key)
	async with engine.begin() as conn:
		await conn.run_sync(Base.metadata.create_all)
          
# ─── Load inspector for a given DB key ─────────────────────────────────────
async def load_inspector(db_key: str):
	"""Get SQLAlchemy inspector for the given DB key."""
	engine = get_engine(db_key)
	async with engine.connect() as conn:
		return await conn.run_sync(inspect)
     

# ─── Merge instance into main and history tables ────────────────────────────
async def _merge_instance(async_ses: AsyncSession, base_instance: Any, cls: type, merged_instance: Any) -> Any:
	"""Helper to merge a single instance into a table."""
	stmt = select(cls).filter_by(description=base_instance.description)
	result = await async_ses.execute(stmt)
	instance = result.scalars().first()

	if instance:
		for k, v in base_instance.__dict__.items():
			if not k.startswith("_") and k != "id":
				setattr(instance, k, v)
	else:
		instance = cls(**{k: v for k, v in base_instance.__dict__.items() if not k.startswith("_")})
		if merged_instance:
			instance.id = merged_instance.id  # Share the ID

	merged_instance = await async_ses.merge(instance)
	await async_ses.flush()
	return merged_instance

async def merge_to_main_and_histo_async_newtrans(
	db_key: str,
	base_instance: Any,
	*classes: type
) -> Any:
	"""Merge an instance into multiple tables (main + history)."""
	merged_instance = None
	async with get_async_session(db_key) as async_ses:
		async with async_ses.begin():
			for cls in classes:
				try:
					merged_instance = await _merge_instance(async_ses, base_instance, cls, merged_instance)
				except Exception as e:
					print(f"Error in merge_to_main_and_histo_async_newtrans for class {cls.__name__}: {e}")
					raise

	#await async_ses.commit()
	return merged_instance

'''
=======================================================================================
ESTA PARTE ES SOLO PARA COMPROBACIONES Y TESTEO DE LA CONEXION A LA BD
=======================================================================================
'''

async def test_db():
	''' Test DB connection and print info 
		Esto sale a raiz del problema que tuve donde la misma conexión a la BD daba
		distinto resultado según la máquina cliente que hacía la conexión:
		Al final resultó ser que había 2 versiones de los ficheros de la BD 
		y según la versión del cliene psql ejecutaba una u otra
	
		Pero parece ser que el cliente psql de una máquina era capaz de ejecutar acciones
		sobre el fichero de la BD a pesar que el servidor estaba parado.
		
		Inicialmente estava la versión de postgres 12 y luego actualicé a la 14 y migré los datos
		desde la 12 a la 14 sin borrar los datos de la versión 12. Paré el servidor 12 pero el cliente psql
		de la máquina seguía funcionando y accediendo a la versión 12.
		
		Al final desinstalé la versión 12 y borré los ficheros de datos de la versión 12 y ya todo funciona bien.	  
		
	'''
	
	async with get_async_session(my_main_db) as session:
		print('0. Compruebe que coinciden todos los datos en casos de comparar conexiones\n')
		
		# 1. Version, usuario, dirección, puerto..
		result = await session.execute(
			text("SELECT current_database(), current_user, version(), inet_server_addr(), inet_server_port()")
		)
		db_name, db_user, db_version, db_addr, db_port = result.first()
		print("1. Conectado a DB:", db_name, "\nversión:", db_version,"\ncon usuario:", db_user,
			"\nen host:", db_addr, "\npuerto:", db_port)
		
		# 2. Ver el directorio la base de datos
		result = await session.execute(text("SHOW data_directory"))
		db_data_directory = result.first()
		print("2. Data directory:", db_data_directory)
		
		# 3. Ver el oid de la base de datos
		result = await session.execute(text("SELECT datname, oid FROM pg_database"))
		db_name, db_oid = result.first()
		print("3. DB_name:", db_name, "DB_oid:", db_oid)
		

		# 4. Ver el search_path
		result = await session.execute(text("SHOW search_path"))
		print("4. Search path:", result.scalar(), "\n")

		# Obtener el engine desde la sesión
		#engine = session.get_bind()
		engine =get_engine(my_main_db)
		print("engineURL:", engine.url)
		print("engine.Dialect:", engine.dialect.name)
		print("engine.Driver:", engine.dialect.driver,"\n")
		print("#######################################################################\n")

		# Usar inspector síncrono dentro del async
		#async with engine.connect() as conn:
		async with engine.begin() as conn:
			def _inspect(sync_conn):
				inspector = inspect(sync_conn)
				schemas = inspector.get_schema_names()
				result = {}
				for schema in schemas:
					tables = inspector.get_table_names(schema=schema)
					result[schema] = tables
				return result

			db_structure = await conn.run_sync(_inspect)

		print("\n--- Estructura de la base de datos ---")
		for schema, tables in db_structure.items():
			print(f"Schema: {schema}")
			for table in tables:
				print(f"   - {table}")



async def schema_and_table_exists_old(session: AsyncSession, schema: str, table: str) -> bool:
	# Get the bound engine
	engine = session.get_bind()

	async with engine.connect() as conn:
		def _check(sync_conn):
			inspector = inspect(sync_conn)
			# List all schemas
			schemas = inspector.get_schema_names()
			if schema not in schemas:
				return False
			# Check if table exists in that schema
			tables = inspector.get_table_names(schema=schema)
			return table in tables

		return await conn.run_sync(_check)

async def schema_and_table_exists(db_key: str, schema: str, table: str) -> bool:
	"""
	Check if a schema and table exist in the database for the given db_key.

	Args:
		db_key (str): The key to select DB connection from async_engines
		schema (str): Schema name to check
		table (str): Table name to check inside the schema

	Returns:
		bool: True if schema and table exist, False otherwise
	"""
	engine = get_engine(db_key)

	async with engine.connect() as conn:
		def _check(sync_conn):
			inspector = inspect(sync_conn)

			# Get all schemas
			schemas = inspector.get_schema_names()
			if schema not in schemas:
				return False

			# Get all tables in schema
			tables = inspector.get_table_names(schema=schema)
			return table in tables

		return await conn.run_sync(_check)		
	


if __name__ == "__main__":
	async def main():
		await test_db()
		print("---------------------------------------------------------------------")
		print("\n--- Checking schema and table existence ---")
		# Check if schema 'ine' and table 'x_users' exist
		exists = await schema_and_table_exists(my_main_db, "ine", "x_users")
		if exists:
			print("✅ Schema 'ine' and table 'x_users' exist")
		else:
			print("❌ Schema 'ine' or table 'x_users' does not exist")

	asyncio.run(main())