"""postgres_fdw USER MAPPING management for gendesign_remote server. USER MAPPING password is in env GENDESIGN_FDW_PASSWORD (not in SQL migration — would leak password to disk). This helper applies idempotent CREATE / ALTER USER MAPPING. Reapplied on every backend startup so password rotation through .env.runtime is picked up after restart. """ from __future__ import annotations import logging from sqlalchemy import text from sqlalchemy.orm import Session from app.core.config import settings logger = logging.getLogger(__name__) def ensure_fdw_user_mapping(db: Session) -> None: """Idempotent CREATE OR ALTER USER MAPPING for tradein → gendesign_remote. Skips silently if password not configured (dev / first deploy). """ password = settings.gendesign_fdw_password if not password: logger.warning( "GENDESIGN_FDW_PASSWORD not set — skipping FDW user mapping " "(gendesign_cad_buildings will fail)" ) return if not isinstance(password, str) or not password.strip(): logger.warning("GENDESIGN_FDW_PASSWORD empty — skipping FDW user mapping") return # Check existence exists = db.execute( text( "SELECT 1 FROM pg_user_mappings " "WHERE srvname = 'gendesign_remote' AND usename = current_user" ) ).first() # Password cannot be a bind parameter in CREATE/ALTER USER MAPPING — must inline. # Escape single quotes by doubling. escaped_pw = password.replace("'", "''") if exists is None: db.execute( text( f"CREATE USER MAPPING FOR CURRENT_USER SERVER gendesign_remote " f"OPTIONS (user 'tradein_fdw_reader', password '{escaped_pw}')" ) ) logger.info("created FDW user mapping for gendesign_remote") else: db.execute( text( f"ALTER USER MAPPING FOR CURRENT_USER SERVER gendesign_remote " f"OPTIONS (SET password '{escaped_pw}')" ) ) logger.info("refreshed FDW user mapping password for gendesign_remote") db.commit()