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/[:] 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" }