diff --git a/service/entityresolution/multi-strategy/README.md b/service/entityresolution/multi-strategy/README.md index 59ee61a019..76c838f110 100644 --- a/service/entityresolution/multi-strategy/README.md +++ b/service/entityresolution/multi-strategy/README.md @@ -691,4 +691,4 @@ See [`example-config.yaml`](./example-config.yaml) for a comprehensive configura - Cross-backend failover scenarios - Health check configuration -For implementation details and architecture decisions, see the [Multi-Strategy ERS ADR](../../../adr/decisions/2025-07-31-multi-strategy-entity-resolution-service.md). \ No newline at end of file +For implementation details and architecture decisions, see the [Multi-Strategy ERS ADR](../../../adr/decisions/2025-07-31-multi-strategy-entity-resolution-service.md). diff --git a/service/entityresolution/multi-strategy/example-config.yaml b/service/entityresolution/multi-strategy/example-config.yaml index f4be018034..8d64dbe9fb 100644 --- a/service/entityresolution/multi-strategy/example-config.yaml +++ b/service/entityresolution/multi-strategy/example-config.yaml @@ -276,4 +276,4 @@ services: # # 5. No matching strategy: # - JWT contains: {"aud": ["unknown"]} -# - Result: Error - no matching strategy found (regardless of failure_strategy) \ No newline at end of file +# - Result: Error - no matching strategy found (regardless of failure_strategy) diff --git a/service/entityresolution/multi-strategy/providers/sql/sql_config.go b/service/entityresolution/multi-strategy/providers/sql/sql_config.go index 31e2439588..8e305f484f 100644 --- a/service/entityresolution/multi-strategy/providers/sql/sql_config.go +++ b/service/entityresolution/multi-strategy/providers/sql/sql_config.go @@ -6,6 +6,10 @@ import ( const ( // Default SQL configuration values + defaultPostgreSQLDriver = "postgres" + canonicalPGXDriver = "pgx/v5" + pgxDriverAlias = "pgx" + postgresQLDriverAlias = "postgresql" defaultPostgreSQLPort = 5432 defaultMaxOpenConnections = 25 defaultMaxIdleConnections = 5 @@ -17,7 +21,7 @@ const ( // SQLConfig defines configuration for SQL database providers type Config struct { // Database connection configuration - Driver string `mapstructure:"driver"` // "postgres", "mysql", "sqlite" + Driver string `mapstructure:"driver"` // "postgres", "mysql", "sqlite" ("pgx", "pgx/v5", and "postgresql" are accepted as aliases) Host string `mapstructure:"host"` Port int `mapstructure:"port"` Database string `mapstructure:"database"` @@ -45,7 +49,7 @@ type Config struct { // DefaultConfig returns a default SQL configuration func DefaultConfig() Config { return Config{ - Driver: "postgres", + Driver: defaultPostgreSQLDriver, Port: defaultPostgreSQLPort, SSLMode: "require", MaxOpenConnections: defaultMaxOpenConnections, diff --git a/service/entityresolution/multi-strategy/providers/sql/sql_config_test.go b/service/entityresolution/multi-strategy/providers/sql/sql_config_test.go new file mode 100644 index 0000000000..3c49729321 --- /dev/null +++ b/service/entityresolution/multi-strategy/providers/sql/sql_config_test.go @@ -0,0 +1,76 @@ +package sql + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestDefaultConfigUsesPostgreSQLDriver(t *testing.T) { + config := DefaultConfig() + require.Equal(t, defaultPostgreSQLDriver, config.Driver) +} + +func TestNormalizeDriverName(t *testing.T) { + tests := []struct { + name string + driver string + want string + }{ + { + name: "pgx", + driver: pgxDriverAlias, + want: canonicalPGXDriver, + }, + { + name: "postgres default", + driver: defaultPostgreSQLDriver, + want: canonicalPGXDriver, + }, + { + name: "postgresql alias with whitespace and case", + driver: " PostgreSQL ", + want: canonicalPGXDriver, + }, + { + name: "other driver", + driver: "mysql", + want: "mysql", + }, + { + name: "sqlite driver", + driver: "sqlite3", + want: "sqlite3", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require.Equal(t, tt.want, normalizeDriverName(tt.driver)) + }) + } +} + +func TestBuildConnectionStringSupportsPostgresAliases(t *testing.T) { + tests := []string{canonicalPGXDriver, defaultPostgreSQLDriver, pgxDriverAlias, postgresQLDriverAlias, "Postgres"} + + for _, driver := range tests { + t.Run(driver, func(t *testing.T) { + provider := &Provider{ + config: Config{ + Driver: driver, + Host: "localhost", + Port: 5432, + Database: "identity_db", + Username: "ers_user", + Password: "ers_password", + SSLMode: "require", + }, + } + + connStr, err := provider.buildConnectionString() + require.NoError(t, err) + require.Contains(t, connStr, "dbname=identity_db") + }) + } +} diff --git a/service/entityresolution/multi-strategy/providers/sql/sql_provider.go b/service/entityresolution/multi-strategy/providers/sql/sql_provider.go index 47f4328edc..f4e139ac2a 100644 --- a/service/entityresolution/multi-strategy/providers/sql/sql_provider.go +++ b/service/entityresolution/multi-strategy/providers/sql/sql_provider.go @@ -6,14 +6,21 @@ import ( "fmt" "strings" - // Database drivers would be imported here: - // _ "github.com/lib/pq" // PostgreSQL driver - // _ "github.com/go-sql-driver/mysql" // MySQL driver - // _ "github.com/mattn/go-sqlite3" // SQLite driver - + // Register the pgx/v5 database/sql driver for SQL providers. + _ "github.com/jackc/pgx/v5/stdlib" "github.com/opentdf/platform/service/entityresolution/multi-strategy/types" ) +func normalizeDriverName(driver string) string { + driver = strings.ToLower(strings.TrimSpace(driver)) + switch driver { + case defaultPostgreSQLDriver, pgxDriverAlias, postgresQLDriverAlias: + return canonicalPGXDriver + default: + return driver + } +} + // Provider implements the Provider interface for SQL databases type Provider struct { name string @@ -24,6 +31,10 @@ type Provider struct { // NewProvider creates a new SQL provider func NewProvider(ctx context.Context, name string, config Config) (*Provider, error) { + // Normalize aliases so "pgx", "postgres", and "postgresql" use the + // registered pgx/v5 database/sql driver name. + config.Driver = normalizeDriverName(config.Driver) + provider := &Provider{ name: name, config: config, @@ -253,8 +264,8 @@ func (p *Provider) Close() error { // buildConnectionString creates a connection string based on the driver func (p *Provider) buildConnectionString() (string, error) { - switch strings.ToLower(p.config.Driver) { - case "postgres": + switch normalizeDriverName(p.config.Driver) { + case canonicalPGXDriver: return fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=%s", p.config.Host, p.config.Port, p.config.Username, p.config.Password, p.config.Database, p.config.SSLMode), nil