Files
relspecgo/pkg/pgsql/connection.go

86 lines
2.0 KiB
Go

package pgsql
import (
"context"
"fmt"
"runtime/debug"
"strings"
"github.com/jackc/pgx/v5"
)
const (
defaultApplicationPrefix = "relspecgo"
postgresIdentifierMaxLen = 63
)
// BuildApplicationName returns a PostgreSQL application_name in the form:
// relspecgo/<version>[:<component>]
func BuildApplicationName(component string) string {
appName := fmt.Sprintf("%s/%s", defaultApplicationPrefix, relspecVersion())
component = strings.TrimSpace(component)
if component != "" {
appName = appName + ":" + component
}
if len(appName) > postgresIdentifierMaxLen {
appName = appName[:postgresIdentifierMaxLen]
}
return appName
}
// ParseConfigWithApplicationName parses a connection string and applies a default
// application_name when one is not explicitly provided by the caller.
func ParseConfigWithApplicationName(connString, component string) (*pgx.ConnConfig, error) {
cfg, err := pgx.ParseConfig(connString)
if err != nil {
return nil, err
}
if cfg.RuntimeParams == nil {
cfg.RuntimeParams = map[string]string{}
}
if strings.TrimSpace(cfg.RuntimeParams["application_name"]) == "" {
cfg.RuntimeParams["application_name"] = BuildApplicationName(component)
}
return cfg, nil
}
// Connect establishes a PostgreSQL connection with a default relspec
// application_name when the caller does not provide one in the DSN.
func Connect(ctx context.Context, connString, component string) (*pgx.Conn, error) {
cfg, err := ParseConfigWithApplicationName(connString, component)
if err != nil {
return nil, err
}
return pgx.ConnectConfig(ctx, cfg)
}
func relspecVersion() string {
info, ok := debug.ReadBuildInfo()
if !ok {
return "dev"
}
version := strings.TrimSpace(info.Main.Version)
if version != "" && version != "(devel)" {
return version
}
for _, setting := range info.Settings {
if setting.Key == "vcs.revision" {
revision := strings.TrimSpace(setting.Value)
if len(revision) >= 7 {
return revision[:7]
}
if revision != "" {
return revision
}
}
}
return "dev"
}