HOWTO · PostgreSQL
Cómo crear un rol o usuario de PostgreSQL si no existe
Cree un rol de PostgreSQL solo cuando sea necesario, gestione de forma segura los errores por roles duplicados y elija un enfoque adecuado para su cliente y sus requisitos de concurrencia.
En esta página
PostgreSQL no ofrece CREATE ROLE IF NOT EXISTS. Para crear un rol con capacidad de inicio de sesión cuando sea necesario, use un bloque PL/pgSQL que capture únicamente duplicate_object, o compruebe antes el catálogo si acepta el riesgo de concurrencia. Un «usuario» de PostgreSQL es un rol con el atributo LOGIN; los roles también pueden ser propietarios de objetos y recibir privilegios.
Crear un rol de PostgreSQL solo si aún no existe
Use CREATE ROLE para definir un rol. Añada LOGIN si debe conectarse a la base de datos y especifique una contraseña si su configuración usa autenticación mediante contraseña. Por ejemplo, el comando SQL es CREATE ROLE app_user LOGIN PASSWORD 'replace-with-a-secret';. Elija una contraseña segura y evite incluir contraseñas de producción en scripts compartidos o en el historial de comandos de la shell.
Si app_user ya existe, PostgreSQL indica que el rol ya existe mediante el estado SQLSTATE 42710 (duplicate_object). Un CREATE ROLE normal no devuelve literalmente el resultado SUCCESSFUL. La documentación oficial de CREATE ROLE describe los atributos disponibles. WITH es sintaxis opcional antes de los atributos del rol; no sustituye a LOGIN.
Los ejemplos SQL de este artículo requieren un servidor PostgreSQL y el entorno de ejecución configurado para el artículo no los ejecuta. Son fragmentos SQL que se deben ejecutar desde un cliente de PostgreSQL con los privilegios adecuados.
Este pequeño ejemplo de Python imprime un comando SQL básico para revisarlo; no se conecta a PostgreSQL ni ejecuta el comando. Ejecute este ejemplo:
sql = "CREATE ROLE app_user LOGIN;"
print(sql)
CREATE ROLE app_user LOGIN;
Gestionar de forma segura los errores por roles duplicados en PL/pgSQL
Si intenta crear el rol directamente y quiere que la operación continúe sin interrupciones cuando otra sesión ya lo haya creado, capture únicamente la condición de objeto duplicado:
DO $$ BEGIN CREATE ROLE app_user LOGIN PASSWORD 'replace-with-a-secret'; EXCEPTION WHEN duplicate_object THEN RAISE NOTICE 'Role app_user already exists; leaving it unchanged.'; END; $$;
Si el rol ya existe, el controlador muestra el aviso y el bloque termina. Si no existe, el comando lo crea. duplicate_object corresponde exactamente al estado SQLSTATE 42710; los errores por privilegios insuficientes o por un comando mal formado no se capturan y, por tanto, se propagan al llamador. Así los fallos reales siguen siendo visibles y no se ocultan otros problemas con un controlador general.
El bloque de excepciones gestiona un intento simultáneo que encuentra la condición de duplicado, por lo que evita la ventana entre una comprobación y la creación posterior. No modifica los atributos de un rol existente: si necesita cambiar su contraseña o sus privilegios, hágalo explícitamente con el comando ALTER ROLE correspondiente.
Comprobar si existe un rol cuando conviene hacer una verificación previa
Una comprobación del catálogo permite omitir el comando de creación cuando el rol ya existe. El catálogo de roles es pg_catalog.pg_roles y rolname contiene el nombre del rol. En PL/pgSQL, la comprobación se puede escribir así: DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_catalog.pg_roles WHERE rolname = 'app_user') THEN CREATE ROLE app_user LOGIN PASSWORD 'replace-with-a-secret'; END IF; END; $$;.
Esto resulta útil cuando necesita realizar otras operaciones según exista o no el rol. Sin embargo, la comprobación por sí sola no es segura frente a la concurrencia: dos sesiones pueden no encontrar ninguna fila coincidente y luego intentar crear el rol. Una de ellas aún podría recibir el estado SQLSTATE 42710. Si debe tolerar intentos de creación simultáneos, use el controlador específico para duplicados descrito antes.
No consulte pg_user como si allí se almacenaran los roles. Use el catálogo de roles documentado, pg_catalog.pg_roles, y compare su columna rolname con el nombre del rol.
Usar psql \gexec para crear un rol de forma condicional
Si ejecuta el script en psql, \gexec puede ejecutar el texto SQL que devuelve una consulta. En el indicador de psql, introduzca esta consulta en una sola línea:
SELECT 'CREATE ROLE app_user LOGIN' WHERE NOT EXISTS (SELECT 1 FROM pg_catalog.pg_roles WHERE rolname = 'app_user')
Después, introduzca el metacomando de psql \gexec en la línea siguiente.
Si el rol no existe, la consulta devuelve un comando CREATE ROLE y psql lo ejecuta. Si el rol ya existe, la consulta no devuelve filas y no hay ningún comando que ejecutar. \gexec es un metacomando de psql, no una instrucción SQL de PostgreSQL; no funcionará si envía el mismo texto mediante un controlador de base de datos genérico u otro cliente SQL. La comprobación previa también puede sufrir una condición de carrera con otra sesión, así que use el manejo específico de excepciones por duplicado si pueden producirse intentos simultáneos. Ejecútelo con una cuenta de base de datos que tenga permiso para crear roles; no hace falta una cuenta raíz del sistema operativo.
Evitar métodos destructivos o engañosos
No use DROP ROLE IF EXISTS app_user seguido de CREATE ROLE app_user como patrón de creación idempotente. Primero elimina el rol existente, lo que puede afectar a la propiedad de objetos, las concesiones de permisos y los objetos dependientes; además, no conserva los atributos del rol.
Evite capturar WHEN OTHERS solo para suprimir un error de duplicado. Ese controlador puede ocultar fallos de permisos, sintaxis y otros problemas operativos. Una comprobación previa de la existencia del rol, un recuento de las filas coincidentes del catálogo o una función personalizada no eliminan la condición de carrera entre comprobar y crear. Mantenga la operación sencilla salvo que la aplicación necesite específicamente una función de base de datos reutilizable, y gestione de forma específica la condición de duplicado cuando la concurrencia sea relevante.
Verificar el rol y elegir un método
Compruebe si existe el rol con SELECT rolname, rolcanlogin FROM pg_catalog.pg_roles WHERE rolname = 'app_user';. Una fila devuelta confirma el nombre y si el rol puede iniciar sesión; si no se devuelve ninguna fila, el rol no existe en la base de datos consultada. Esta consulta sirve para verificar, pero no garantiza que un comando de creación posterior esté libre de condiciones de carrera.
Elija el método según cómo vaya a ejecutar la operación:
- Use el controlador
duplicate_objectpara intentar crear el rol directamente, tolerar intentos simultáneos y dejar intacto un rol existente. - Use una comprobación previa del catálogo cuando la lógica circundante deba ramificarse según exista el rol y la creación simultánea no sea un problema, o cuando gestione los errores de duplicado por separado.
- Use
\gexecpara ejecutar un comando condicional conciso enpsql, teniendo en cuenta el mismo riesgo de carrera de la comprobación previa.
Para crear el rol correctamente, la cuenta de base de datos debe tener los privilegios necesarios. Si necesita cambiar un rol existente, use instrucciones explícitas para modificarlo en lugar de eliminarlo y volverlo a crear.
Conclusión
PostgreSQL no tiene una forma CREATE ROLE IF NOT EXISTS. Para intentar crear un rol de forma segura solo cuando no existe, capture únicamente duplicate_object en PL/pgSQL; una comprobación previa del catálogo o \gexec en psql pueden ser opciones prácticas si las limitaciones del cliente y de concurrencia se ajustan a su caso. Verifique el rol en pg_catalog.pg_roles y conserve los roles existentes salvo que realmente pretenda eliminarlos o modificarlos.