from psycopg import sql from psycopg.rows import dict_row from .connection import ( admin_connection, close_project_pool, get_project_pool, is_project_pool_open, ) _server_databases = ["template0", "template1", "postgres", "project"] def list_project() -> list[str]: ps = [] with admin_connection() as conn: with conn.cursor(row_factory=dict_row) as cur: for p in cur.execute( "select datname from pg_database where datname <> all(%s) order by datname", (_server_databases,), ): ps.append(p["datname"]) return ps def have_project(name: str) -> bool: with admin_connection() as conn: with conn.cursor() as cur: cur.execute("select 1 from pg_database where datname = %s", (name,)) return cur.fetchone() is not None def copy_project(source: str, new: str) -> None: close_project_pool(source) with admin_connection() as admin_conn: with admin_conn.cursor() as cur: cur.execute( "update pg_database set datallowconn = false where datname = %s", (source,), ) try: cur.execute( "select pg_terminate_backend(pid) from pg_stat_activity where datname = %s and pid <> pg_backend_pid()", (source,), ) cur.execute( sql.SQL("create database {} with template = {}").format( sql.Identifier(new), sql.Identifier(source) ) ) finally: cur.execute( "update pg_database set datallowconn = true where datname = %s", (source,), ) def create_project(name: str) -> None: return copy_project("project", name) def delete_project(name: str) -> None: close_project_pool(name) with admin_connection() as conn: with conn.cursor() as cur: cur.execute( "select pg_terminate_backend(pid) from pg_stat_activity " "where datname = %s and pid <> pg_backend_pid()", (name,), ) cur.execute( sql.SQL("drop database {}").format(sql.Identifier(name)) ) def clean_project(excluded: list[str] = []) -> None: projects = list_project() with admin_connection() as conn: with conn.cursor(row_factory=dict_row) as cur: row = cur.execute("select current_database()").fetchone() if row != None: current_db = row["current_database"] if current_db in projects: projects.remove(current_db) for project in projects: if project in _server_databases or project in excluded: continue cur.execute( "select pg_terminate_backend(pid) from pg_stat_activity " "where datname = %s and pid <> pg_backend_pid()", (project,), ) cur.execute( sql.SQL("drop database {}").format(sql.Identifier(project)) ) def open_project(name: str) -> None: get_project_pool(name) def is_project_open(name: str) -> bool: return is_project_pool_open(name) def close_project(name: str) -> None: close_project_pool(name)