import psycopg2 import psycopg2.extras import psycopg2.pool from flask import g, current_app # Module-level connection pool (initialised once per process) _pool = None def init_db(app): """Initialise the connection pool on startup.""" global _pool try: _pool = psycopg2.pool.SimpleConnectionPool( minconn=2, maxconn=10, dsn=app.config["DATABASE_URL"], ) print(" * Database connection pool OK (2–10 connections)") except Exception as e: print(f" * Database connection pool FAILED: {e}") def get_db(): """Get a pooled database connection for the current request.""" if "db" not in g: g.db = _pool.getconn() g.db.cursor_factory = psycopg2.extras.RealDictCursor return g.db def close_db(exception=None): """Return database connection to the pool at end of request.""" db = g.pop("db", None) if db is not None: if exception: db.rollback() _pool.putconn(db) def query(sql, params=None): """Execute a SELECT query and return all rows as dicts.""" db = get_db() with db.cursor() as cur: cur.execute(sql, params) return cur.fetchall() def query_one(sql, params=None): """Execute a SELECT query and return one row as a dict.""" db = get_db() with db.cursor() as cur: cur.execute(sql, params) return cur.fetchone() def execute(sql, params=None): """Execute an INSERT/UPDATE/DELETE and commit.""" db = get_db() with db.cursor() as cur: cur.execute(sql, params) db.commit() def execute_returning(sql, params=None): """Execute an INSERT/UPDATE/DELETE with RETURNING and commit.""" db = get_db() with db.cursor() as cur: cur.execute(sql, params) row = cur.fetchone() db.commit() return row def execute_many(sql, params_list): """Execute a batch INSERT/UPDATE/DELETE and commit.""" db = get_db() with db.cursor() as cur: cur.executemany(sql, params_list) db.commit()