feat(pgsql): implement application_name handling in connection
This commit is contained in:
85
pkg/pgsql/connection.go
Normal file
85
pkg/pgsql/connection.go
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
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"
|
||||||
|
}
|
||||||
@@ -664,7 +664,7 @@ func (r *Reader) parseColumn(line, tableName, schemaName string) (*models.Column
|
|||||||
return column, constraint
|
return column, constraint
|
||||||
}
|
}
|
||||||
|
|
||||||
func splitInlineComment(line string) (string, string) {
|
func splitInlineComment(line string) (content string, inlineComment string) {
|
||||||
commentStart := strings.Index(line, "//")
|
commentStart := strings.Index(line, "//")
|
||||||
if commentStart == -1 {
|
if commentStart == -1 {
|
||||||
return line, ""
|
return line, ""
|
||||||
@@ -673,7 +673,7 @@ func splitInlineComment(line string) (string, string) {
|
|||||||
return strings.TrimSpace(line[:commentStart]), strings.TrimSpace(line[commentStart+2:])
|
return strings.TrimSpace(line[:commentStart]), strings.TrimSpace(line[commentStart+2:])
|
||||||
}
|
}
|
||||||
|
|
||||||
func splitColumnSignatureAndAttrs(line string) (string, string) {
|
func splitColumnSignatureAndAttrs(line string) (signature string, attrs string) {
|
||||||
trimmed := strings.TrimSpace(line)
|
trimmed := strings.TrimSpace(line)
|
||||||
if trimmed == "" || !strings.HasSuffix(trimmed, "]") {
|
if trimmed == "" || !strings.HasSuffix(trimmed, "]") {
|
||||||
return trimmed, ""
|
return trimmed, ""
|
||||||
@@ -699,7 +699,7 @@ func splitColumnSignatureAndAttrs(line string) (string, string) {
|
|||||||
return trimmed, ""
|
return trimmed, ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseColumnSignature(signature string) (string, string, bool) {
|
func parseColumnSignature(signature string) (columnName string, columnType string, ok bool) {
|
||||||
signature = strings.TrimSpace(signature)
|
signature = strings.TrimSpace(signature)
|
||||||
if signature == "" {
|
if signature == "" {
|
||||||
return "", "", false
|
return "", "", false
|
||||||
@@ -726,8 +726,8 @@ func parseColumnSignature(signature string) (string, string, bool) {
|
|||||||
return "", "", false
|
return "", "", false
|
||||||
}
|
}
|
||||||
|
|
||||||
columnName := stripQuotes(strings.TrimSpace(signature[:splitAt]))
|
columnName = stripQuotes(strings.TrimSpace(signature[:splitAt]))
|
||||||
columnType := stripWrappingQuotes(strings.TrimSpace(signature[splitAt:]))
|
columnType = stripWrappingQuotes(strings.TrimSpace(signature[splitAt:]))
|
||||||
if columnName == "" || columnType == "" {
|
if columnName == "" || columnType == "" {
|
||||||
return "", "", false
|
return "", "", false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,11 +5,10 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/jackc/pgx/v5"
|
|
||||||
|
|
||||||
"git.warky.dev/wdevs/relspecgo/pkg/models"
|
"git.warky.dev/wdevs/relspecgo/pkg/models"
|
||||||
"git.warky.dev/wdevs/relspecgo/pkg/pgsql"
|
"git.warky.dev/wdevs/relspecgo/pkg/pgsql"
|
||||||
"git.warky.dev/wdevs/relspecgo/pkg/readers"
|
"git.warky.dev/wdevs/relspecgo/pkg/readers"
|
||||||
|
"github.com/jackc/pgx/v5"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Reader implements the readers.Reader interface for PostgreSQL databases
|
// Reader implements the readers.Reader interface for PostgreSQL databases
|
||||||
@@ -244,7 +243,7 @@ func (r *Reader) ReadTable() (*models.Table, error) {
|
|||||||
|
|
||||||
// connect establishes a connection to the PostgreSQL database
|
// connect establishes a connection to the PostgreSQL database
|
||||||
func (r *Reader) connect() error {
|
func (r *Reader) connect() error {
|
||||||
conn, err := pgx.Connect(r.ctx, r.options.ConnectionString)
|
conn, err := pgsql.Connect(r.ctx, r.options.ConnectionString, "reader-pgsql")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,8 +10,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/jackc/pgx/v5"
|
|
||||||
|
|
||||||
"git.warky.dev/wdevs/relspecgo/pkg/models"
|
"git.warky.dev/wdevs/relspecgo/pkg/models"
|
||||||
"git.warky.dev/wdevs/relspecgo/pkg/pgsql"
|
"git.warky.dev/wdevs/relspecgo/pkg/pgsql"
|
||||||
"git.warky.dev/wdevs/relspecgo/pkg/writers"
|
"git.warky.dev/wdevs/relspecgo/pkg/writers"
|
||||||
@@ -1353,7 +1351,7 @@ func (w *Writer) executeDatabaseSQL(db *models.Database, connString string) erro
|
|||||||
|
|
||||||
// Connect to database
|
// Connect to database
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
conn, err := pgx.Connect(ctx, connString)
|
conn, err := pgsql.Connect(ctx, connString, "writer-pgsql")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to connect to database: %w", err)
|
return fmt.Errorf("failed to connect to database: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user