package database import ( "errors" "fmt" "net/url" "os" "strconv" "strings" "github.com/jmoiron/sqlx" // postgresql driver. _ "github.com/jackc/pgx/v5/stdlib" ) const ( databaseDSNEnvVar = "BUNKERD_DATABASE_DSN" databaseMaxIdleConnsEnvVar = "BUNKERD_DATABASE_MAX_IDLE_CONNS" databaseMaxOpenedConnsEnvVar = "BUNKERD_DATABASE_MAX_OPENED_CONNS" ) var ( errDSNInvalid = errors.New("BUNKERD_DATABASE_DSN environment variable is empty or invalid") errNoMaxIdleConns = errors.New("no BUNKERD_DATABASE_MAX_IDLE_CONNS defined") errNoMaxOpenedConns = errors.New("no BUNKERD_DATABASE_MAX_OPENED_CONNS defined") errPostgresOnlySupported = errors.New("only PostgreSQL database is currently supported") ) func (d *database) initializeConnection() error { // Getting database DSN from environment as well as other required settings. dsn, found := os.LookupEnv(databaseDSNEnvVar) if !found { return fmt.Errorf("initialize connection: getting database DSN: %w", errDSNInvalid) } maxOpenedConnsRaw, found := os.LookupEnv(databaseMaxOpenedConnsEnvVar) if !found { return fmt.Errorf("initialize connection: getting maximum number of opened conections: %w", errNoMaxOpenedConns) } maxOpenedConns, err := strconv.ParseInt(maxOpenedConnsRaw, 10, 64) if err != nil { return fmt.Errorf("initialize connection: parsing maximum number of opened conections: %w", err) } maxIdleConnsRaw, found := os.LookupEnv(databaseMaxIdleConnsEnvVar) if !found { return fmt.Errorf("initialize connection: getting maximum number of idle conections: %w", errNoMaxIdleConns) } maxIdleConns, err := strconv.ParseInt(maxIdleConnsRaw, 10, 64) if err != nil { return fmt.Errorf("initialize connection: parsing maximum number of opened conections: %w", err) } // While database/sql (and sqlx) supports all possible DSN formats, we will force user to use DSN in form // "proto://user:passowrd@host:port/dbname" as it is easier to parse. if _, err := url.Parse(dsn); err != nil { return fmt.Errorf("initialize connection: validate DSN: %w", err) } // Currently we're support only postgresql, but this may change in future. if !strings.HasPrefix(dsn, "postgres://") { return fmt.Errorf("initialize connection: validate DSN: %w", errPostgresOnlySupported) } proto := strings.Split(dsn, ":")[0] if proto == "postgres" { proto = "pgx" } db, err := sqlx.Open(proto, dsn) if err != nil { return fmt.Errorf("initialize connection: open database: %w", err) } d.db = db d.db.SetMaxOpenConns(int(maxOpenedConns)) d.db.SetMaxIdleConns(int(maxIdleConns)) return nil }