117 lines
3.0 KiB
Go
117 lines
3.0 KiB
Go
package postgres
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/url"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/jackc/pgx/v5"
|
|
"github.com/jackc/pgx/v5/pgxpool"
|
|
)
|
|
|
|
type Module struct {
|
|
pool Pool
|
|
Store *Store
|
|
}
|
|
|
|
func (module *Module) Close() {
|
|
if module != nil && module.pool != nil {
|
|
module.pool.Close()
|
|
}
|
|
}
|
|
|
|
func Open(ctx context.Context, config Config) (*Module, error) {
|
|
if config.Backend == BackendLocal {
|
|
return &Module{Store: NewDatabase(config, nil)}, nil
|
|
}
|
|
if config.Backend != BackendPostgres {
|
|
return nil, fmt.Errorf("unsupported data backend %q", config.Backend)
|
|
}
|
|
databaseURL, err := forcePlaintextDatabaseURL(config.DatabaseURL)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("parse DATABASE_URL: %w", err)
|
|
}
|
|
poolConfig, err := pgxpool.ParseConfig(databaseURL)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("parse DATABASE_URL: %w", err)
|
|
}
|
|
poolConfig.MaxConns = config.PoolMax
|
|
poolConfig.MaxConnIdleTime = config.IdleTimeout
|
|
poolConfig.ConnConfig.ConnectTimeout = config.ConnectionTimeout
|
|
poolConfig.ConnConfig.RuntimeParams["statement_timeout"] = strconv.FormatInt(config.StatementTimeout.Milliseconds(), 10)
|
|
poolConfig.ConnConfig.RuntimeParams["application_name"] = config.ApplicationName
|
|
poolConfig.ConnConfig.TLSConfig = nil
|
|
poolConfig.ConnConfig.Fallbacks = nil
|
|
pool, err := pgxpool.NewWithConfig(ctx, poolConfig)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("open PostgreSQL pool: %w", err)
|
|
}
|
|
adapter := &pgxPoolAdapter{pool: pool}
|
|
return &Module{pool: adapter, Store: NewDatabase(config, adapter)}, nil
|
|
}
|
|
|
|
func forcePlaintextDatabaseURL(databaseURL string) (string, error) {
|
|
parsed, err := url.Parse(databaseURL)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
query := parsed.Query()
|
|
for key := range query {
|
|
switch strings.ToLower(key) {
|
|
case "sslmode", "sslrootcert":
|
|
query.Del(key)
|
|
}
|
|
}
|
|
query.Set("sslmode", string(SSLDisable))
|
|
parsed.RawQuery = query.Encode()
|
|
return parsed.String(), nil
|
|
}
|
|
|
|
type pgxPoolAdapter struct {
|
|
pool *pgxpool.Pool
|
|
}
|
|
|
|
func (p *pgxPoolAdapter) Query(ctx context.Context, sql string, args ...any) (Rows, error) {
|
|
return p.pool.Query(ctx, sql, args...)
|
|
}
|
|
|
|
func (p *pgxPoolAdapter) Begin(ctx context.Context) (Transaction, error) {
|
|
tx, err := p.pool.Begin(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &pgxTransactionAdapter{tx: tx}, nil
|
|
}
|
|
|
|
func (p *pgxPoolAdapter) Close() {
|
|
p.pool.Close()
|
|
}
|
|
|
|
var _ Pool = (*pgxPoolAdapter)(nil)
|
|
var _ TransactionBeginner = (*pgxPoolAdapter)(nil)
|
|
|
|
type pgxTransactionAdapter struct {
|
|
tx pgx.Tx
|
|
}
|
|
|
|
func (t *pgxTransactionAdapter) Query(ctx context.Context, sql string, args ...any) (Rows, error) {
|
|
return t.tx.Query(ctx, sql, args...)
|
|
}
|
|
|
|
func (t *pgxTransactionAdapter) Exec(ctx context.Context, sql string, args ...any) error {
|
|
_, err := t.tx.Exec(ctx, sql, args...)
|
|
return err
|
|
}
|
|
|
|
func (t *pgxTransactionAdapter) Commit(ctx context.Context) error {
|
|
return t.tx.Commit(ctx)
|
|
}
|
|
|
|
func (t *pgxTransactionAdapter) Rollback(ctx context.Context) error {
|
|
return t.tx.Rollback(ctx)
|
|
}
|
|
|
|
var _ Transaction = (*pgxTransactionAdapter)(nil)
|