Migración del clúster PostgreSQL de SIU-Guaraní: guía y cuidados con los permisos

Mover una base de datos de Guaraní a otro host no es solo «copiar y restaurar». El error clásico que aparece después de la restauración es Undefined table o permiso denegado, incluso cuando los datos están intactos. La causa casi siempre está en los roles, los search_path y la jerarquía de permisos de PostgreSQL, que no viajan automáticamente con los datos.

Esta guía resume el flujo recomendado y los puntos críticos que debes verificar para que Guaraní funcione en el nuevo servidor.

Estrategia de volcado: no uses solo pg_dumpall

La documentación oficial de SIU-Guaraní para migrar a un nuevo servidor propone un flujo controlado: generar una instalación limpia de Guaraní en el host nuevo, hacer el dump de la base de datos vieja, restaurarla con el nombre que tenía la instalación nueva, y luego regenerar la instancia y migrar la base.

Recomendación práctica: separar los objetos globales (roles y tablespaces) de los datos de negocio.

Exportar solo roles y tablespaces (globales)

pg_dumpall --globals-only -U postgres -f globales.sql

Exportar la base de datos de Guaraní por separado (formato custom, comprimido)

pg_dump -U postgres -Fc -b -v -f BD-GUARANI.dump BD-GUARANI

Esto evita el problema más común: restaurar el dump completo antes de que los roles existan, lo que hace que los ALTER … OWNER TO fallen o se omitan.

El orden de restauración importa (y mucho)

El orden correcto en el host destino es:

Crear los roles primero

psql -f globales.sql

Crear la base de datos vacía (con createdb, usando template0 para evitar dependencias de template1).

Restaurar el dump de la base con

pg_restore -d BD-GUARANI-NUEVA BD-GUARANI.dump

Si restauras el dump completo con pg_dumpall sin haber creado los roles antes, PostgreSQL puede abortar la carga de datos o dejar las tablas sin dueño. El síntoma típico es que pg_tables devuelve 0 filas en la base recién restaurada.

El problema del search_path: la tabla existe pero «no existe»

Guaraní y Toba consultan tablas sin calificar el esquema (FROM app_versiones_base, no FROM negocio.app_versiones_base). PostgreSQL resuelve ese nombre usando el search_path de la sesión.

Cuando recreas un rol a mano, este hereda el search_path predeterminado: «$user», public. Si el esquema negocio no está ahí, la tabla es invisible para la aplicación, aunque exista y el usuario tenga permisos sobre ella.

Solución persistente: fijar el search_path en el rol que usa Guaraní.

ALTER ROLE usuario-guarani SET search_path TO negocio, public;

Verificar con:

SHOW search_path;

Debe mostrar: negocio, public

Si además la aplicación usa el esquema desarrollo para metadatos de Toba, puedes incluir ambos:

ALTER ROLE usuario-guarani SET search_path TO negocio, desarrollo, public;

Nota: Toba también lee el campo schema del bases.ini. Si la configuración original decía schema = «negocio», Toba debería establecer el contexto, pero fijarlo a nivel de rol es más robusto y sobrevive a reconexiones y pools de conexión.

Jerarquía de permisos: USAGE sobre el esquema + SELECT sobre la tabla

En PostgreSQL los permisos son jerárquicos. Para leer una tabla necesitas dos cosas:

Nivel

Permiso

Qué controla

Esquema

USAGE

Permite «entrar» al esquema y resolver nombres de objetos

Tabla

SELECT

Permite leer los datos de la tabla

Si falta USAGE, el error es permiso denegado al esquema. Si falta SELECT, el error es permiso denegado a la tabla.

Comandos para asignar ambos niveles (ejecutar como superusuario, por cada esquema que use Guaraní):

-- Para el esquema de negocio
GRANT USAGE ON SCHEMA negocio TO usuario-guarani;
GRANT SELECT ON ALL TABLES IN SCHEMA negocio TO usuario-guarani;
-- Para el esquema de metadatos de Toba (si aplica)
GRANT USAGE ON SCHEMA desarrollo TO usuario-guarani;
GRANT SELECT ON ALL TABLES IN SCHEMA desarrollo TO usuario-guarani;

El futuro: tablas que se crearán después

GRANT ... ON ALL TABLES IN SCHEMA solo afecta a las tablas que ya existen. Si Guaraní o Toba crean nuevas tablas durante una migración o actualización, el usuario nuevo no tendrá permisos automáticamente.

Para evitarlo, configurar privilegios predeterminados ejecutando el comando como el propietario de las tablas (normalmente el usuario que ejecuta las migraciones de Guaraní, o postgres si es el dueño original):

ALTER DEFAULT PRIVILEGES IN SCHEMA negocio GRANT SELECT ON TABLES TO usuario-guarani;
ALTER DEFAULT PRIVILEGES IN SCHEMA desarrollo GRANT SELECT ON TABLES TO usuario-guarani;

Esto garantiza que las tablas creadas en el futuro hereden el SELECT para el usuario .

Verificación final

Conectar con el usuario nuevo y ejecutar estas pruebas:

-- 1. El search_path es correcto
SHOW search_path;


-- 2. La tabla de negocio es visible sin prefijo
SELECT trim(version_app) FROM app_versiones_base ORDER BY id_conversion DESC LIMIT 1;

-- 3. La tabla de metadatos de Toba es visible
SELECT * FROM apex_proyecto LIMIT 1;

Si las tres consultas funcionan sin errores, la aplicación web debería cargar sin los Undefined table ni permiso denegado.

Chequeo rápido de migración

  • [ ] ¿Exportaste los roles con pg_dumpall --globals-only antes de tocar la base de datos?

  • [ ] ¿Creaste los roles en el destino antes de restaurar el dump de negocio?

  • [ ] ¿Fijaste ALTER ROLE … SET search_path con los esquemas negocio (y desarrollo si aplica)?

  • [ ] ¿Otorgaste USAGE sobre cada esquema y SELECT sobre cada tabla?

  • [ ] ¿Configuraste ALTER DEFAULT PRIVILEGES para tablas futuras?

  • [ ] ¿Actualizaste el bases.ini con el nuevo usuario y contraseña en todos los bloques que apuntan a BD-GUARANI?

  • [ ] ¿Reiniciaste el servicio web o PHP-FPM después de cambiar bases.ini?

Con estos pasos, la migración se convierte en un procedimiento reproducible.