package pgdb import ( "context" "crypto/tls" "crypto/x509" "database/sql" "fmt" "os" "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/stdlib" "github.com/jmoiron/sqlx" ) // Интерфейс передается в неэкспортируемые функции // нужен для того, чтобы не зависило от того, что передаём // транзацию или обычное соединение type Driver interface { // QueryContext executes a query that returns rows, typically a SELECT. // The args are for any placeholder parameters in the query. QueryContext(ctx context.Context, query string, args ...any) (*sql.Rows, error) // QueryRowContext executes a query that is expected to return at most one row. // QueryRowContext always returns a non-nil value. Errors are deferred until // [Row]'s Scan method is called. // If the query selects no rows, the [*Row.Scan] will return [ErrNoRows]. // Otherwise, [*Row.Scan] scans the first selected row and discards // the rest. QueryRowContext(ctx context.Context, query string, args ...any) *sql.Row // ExecContext executes a query without returning any rows. // The args are for any placeholder parameters in the query. ExecContext(ctx context.Context, query string, args ...any) (sql.Result, error) } type PostgresConfig struct { Host string Port uint16 Username string Password string Database string Schema string SSLMode string SSLRootCert string } type client struct { config PostgresConfig db *sqlx.DB } func NewClient(cfg PostgresConfig) (*client, error) { rootCertPool := x509.NewCertPool() caCert, err := os.ReadFile(cfg.SSLRootCert) if err != nil { return nil, fmt.Errorf("failed to read CA cert: %w", err) } if ok := rootCertPool.AppendCertsFromPEM(caCert); !ok { return nil, fmt.Errorf("failed to append CA cert to pool") } tlsConfig := &tls.Config{ RootCAs: rootCertPool, MinVersion: tls.VersionTLS12, //nolint:gosec // TODO: set server name InsecureSkipVerify: true, } config, err := pgx.ParseConfig("") if err != nil { return nil, fmt.Errorf("failed to parse [empty] config: %w", err) } config.Host = cfg.Host config.Port = cfg.Port config.Database = cfg.Database config.User = cfg.Username config.Password = cfg.Password config.TLSConfig = tlsConfig config.RuntimeParams = map[string]string{"sslmode": cfg.SSLMode} db := sqlx.NewDb(stdlib.OpenDB(*config), "pgx") if err := db.Ping(); err != nil { return nil, fmt.Errorf("failed to ping postgres: %w", err) } return &client{ config: cfg, db: db, }, nil } func countOffset(page, pageSize uint64) uint64 { if page == 0 { page = 1 } if pageSize == 0 { pageSize = DefaultPaginationPageSize } return (page - 1) * pageSize }