package database import ( "context" "fmt" "time" "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgconn" "github.com/jackc/pgx/v5/pgxpool" "github.com/gosec/gsc-ops-api/internal/config" ) // DB wraps the database connection pool type DB struct { pool *pgxpool.Pool } // New creates a new database connection pool using the legacy config func New(cfg *config.Config) (*DB, error) { return NewFromDSN(cfg.DatabaseDSN(), cfg.Database.MaxConns, cfg.Database.MinConns) } // NewNamed creates a new database connection pool for a named database func NewNamed(cfg *config.Config, name string) (*DB, error) { db, ok := cfg.Databases[name] if !ok { return nil, fmt.Errorf("database %q not configured", name) } return NewFromDSN(cfg.NamedDatabaseDSN(name), db.MaxConns, db.MinConns) } // NewFromDSN creates a new database connection pool from a DSN string func NewFromDSN(dsn string, maxConns, minConns int) (*DB, error) { poolConfig, err := pgxpool.ParseConfig(dsn) if err != nil { return nil, fmt.Errorf("failed to parse database config: %w", err) } poolConfig.MaxConns = int32(maxConns) poolConfig.MinConns = int32(minConns) poolConfig.MaxConnLifetime = 1 * time.Hour poolConfig.MaxConnIdleTime = 30 * time.Minute poolConfig.HealthCheckPeriod = 1 * time.Minute ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() pool, err := pgxpool.NewWithConfig(ctx, poolConfig) if err != nil { return nil, fmt.Errorf("failed to create connection pool: %w", err) } if err := pool.Ping(ctx); err != nil { pool.Close() return nil, fmt.Errorf("failed to ping database: %w", err) } return &DB{pool: pool}, nil } func (db *DB) Close() { if db.pool != nil { db.pool.Close() } } func (db *DB) Pool() *pgxpool.Pool { return db.pool } func (db *DB) Query(ctx context.Context, sql string, args ...interface{}) (pgx.Rows, error) { return db.pool.Query(ctx, sql, args...) } func (db *DB) QueryRow(ctx context.Context, sql string, args ...interface{}) pgx.Row { return db.pool.QueryRow(ctx, sql, args...) } func (db *DB) Exec(ctx context.Context, sql string, args ...interface{}) (pgconn.CommandTag, error) { return db.pool.Exec(ctx, sql, args...) } func (db *DB) Health(ctx context.Context) error { return db.pool.Ping(ctx) } func (db *DB) Stats() *pgxpool.Stat { return db.pool.Stat() }