104 lines
2.4 KiB
Go
104 lines
2.4 KiB
Go
package postgresadmin
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"reflect"
|
|
|
|
"github.com/jackc/pgx/v5"
|
|
"github.com/jackc/pgx/v5/pgconn"
|
|
|
|
"proxy-pool/internal/domain/adminstate"
|
|
)
|
|
|
|
var _ adminstate.Store = (*adapter)(nil)
|
|
|
|
type transactionBeginner interface {
|
|
BeginTx(context.Context, pgx.TxOptions) (pgx.Tx, error)
|
|
}
|
|
|
|
// adapter keeps all PostgreSQL transaction and SQL details behind adminstate.Store.
|
|
type adapter struct {
|
|
pool transactionBeginner
|
|
}
|
|
|
|
// New constructs the PostgreSQL management-state store.
|
|
func New(pool transactionBeginner) (adminstate.Store, error) {
|
|
if isNil(pool) {
|
|
return nil, adminstate.ErrInvalidCommand
|
|
}
|
|
return &adapter{pool: pool}, nil
|
|
}
|
|
|
|
func isNil(value any) bool {
|
|
if value == nil {
|
|
return true
|
|
}
|
|
kind := reflect.ValueOf(value).Kind()
|
|
return (kind == reflect.Chan || kind == reflect.Func || kind == reflect.Interface ||
|
|
kind == reflect.Map || kind == reflect.Pointer || kind == reflect.Slice) &&
|
|
reflect.ValueOf(value).IsNil()
|
|
}
|
|
|
|
func contextError(ctx context.Context) error {
|
|
if ctx == nil {
|
|
return adminstate.ErrInvalidCommand
|
|
}
|
|
return ctx.Err()
|
|
}
|
|
|
|
func (adapter *adapter) valid() bool {
|
|
return adapter != nil && !isNil(adapter.pool)
|
|
}
|
|
|
|
func (adapter *adapter) begin(ctx context.Context, options pgx.TxOptions, operation string) (pgx.Tx, error) {
|
|
tx, err := adapter.pool.BeginTx(ctx, options)
|
|
if err != nil {
|
|
return nil, databaseError(ctx, operation, err)
|
|
}
|
|
if isNil(tx) {
|
|
return nil, unavailable(operation)
|
|
}
|
|
return tx, nil
|
|
}
|
|
|
|
func rollback(tx pgx.Tx) {
|
|
if !isNil(tx) {
|
|
_ = tx.Rollback(context.Background())
|
|
}
|
|
}
|
|
|
|
func commit(ctx context.Context, tx pgx.Tx, operation string) error {
|
|
if err := tx.Commit(ctx); err != nil {
|
|
return databaseError(ctx, operation, err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func databaseError(ctx context.Context, operation string, err error) error {
|
|
if err == nil {
|
|
return nil
|
|
}
|
|
if ctx != nil && ctx.Err() != nil {
|
|
return ctx.Err()
|
|
}
|
|
if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
|
|
return err
|
|
}
|
|
|
|
sentinel := adminstate.ErrUnavailable
|
|
var postgresError *pgconn.PgError
|
|
if errors.As(err, &postgresError) {
|
|
switch postgresError.Code {
|
|
case "23505":
|
|
sentinel = adminstate.ErrConflict
|
|
}
|
|
}
|
|
return fmt.Errorf("postgresadmin: %s: %w", operation, sentinel)
|
|
}
|
|
|
|
func unavailable(operation string) error {
|
|
return fmt.Errorf("postgresadmin: %s: %w", operation, adminstate.ErrUnavailable)
|
|
}
|