@@ -17,6 +17,8 @@ import (
17
17
"context"
18
18
"errors"
19
19
"fmt"
20
+ "net/url"
21
+ "strings"
20
22
"time"
21
23
22
24
"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
@@ -32,6 +34,13 @@ import (
32
34
// PostgresAuthMetadata contains authentication metadata for PostgreSQL components.
33
35
type PostgresAuthMetadata struct {
34
36
ConnectionString string `mapstructure:"connectionString" mapstructurealiases:"url"`
37
+ Host string `mapstructure:"host"`
38
+ HostAddr string `mapstructure:"hostaddr"`
39
+ Port string `mapstructure:"port"`
40
+ Database string `mapstructure:"database"`
41
+ User string `mapstructure:"user"`
42
+ Password string `mapstructure:"password"`
43
+ SslRootCert string `mapstructure:"sslRootCert"`
35
44
ConnectionMaxIdleTime time.Duration `mapstructure:"connectionMaxIdleTime"`
36
45
MaxConns int `mapstructure:"maxConns"`
37
46
UseAzureAD bool `mapstructure:"useAzureAD"`
@@ -45,6 +54,13 @@ type PostgresAuthMetadata struct {
45
54
// Reset the object.
46
55
func (m * PostgresAuthMetadata ) Reset () {
47
56
m .ConnectionString = ""
57
+ m .Host = ""
58
+ m .HostAddr = ""
59
+ m .Port = ""
60
+ m .Database = ""
61
+ m .User = ""
62
+ m .Password = ""
63
+ m .SslRootCert = ""
48
64
m .ConnectionMaxIdleTime = 0
49
65
m .MaxConns = 0
50
66
m .UseAzureAD = false
@@ -62,8 +78,9 @@ type InitWithMetadataOpts struct {
62
78
// This is different from the "useAzureAD" property from the user, which is provided by the user and instructs the component to authenticate using Azure AD.
63
79
func (m * PostgresAuthMetadata ) InitWithMetadata (meta map [string ]string , opts InitWithMetadataOpts ) (err error ) {
64
80
// Validate input
65
- if m .ConnectionString == "" {
66
- return errors .New ("missing connection string" )
81
+ _ , err = m .buildConnectionString ()
82
+ if err != nil {
83
+ return err
67
84
}
68
85
switch {
69
86
case opts .AzureADEnabled && m .UseAzureAD :
@@ -87,6 +104,118 @@ func (m *PostgresAuthMetadata) InitWithMetadata(meta map[string]string, opts Ini
87
104
return nil
88
105
}
89
106
107
+ // buildConnectionString builds the connection string from the metadata.
108
+ // It supports both DSN-style and URL-style connection strings.
109
+ // Metadata fields override existing values in the connection string.
110
+ func (m * PostgresAuthMetadata ) buildConnectionString () (string , error ) {
111
+ metadata := m .getConnectionStringMetadata ()
112
+ if strings .HasPrefix (m .ConnectionString , "postgres://" ) || strings .HasPrefix (m .ConnectionString , "postgresql://" ) {
113
+ return m .buildURLConnectionString (metadata )
114
+ }
115
+ return m .buildDSNConnectionString (metadata )
116
+ }
117
+
118
+ func (m * PostgresAuthMetadata ) buildDSNConnectionString (metadata map [string ]string ) (string , error ) {
119
+ connectionString := ""
120
+ parts := strings .Split (m .ConnectionString , " " )
121
+ for _ , part := range parts {
122
+ kv := strings .SplitN (part , "=" , 2 )
123
+ if len (kv ) == 2 {
124
+ key := kv [0 ]
125
+ if value , ok := metadata [key ]; ok {
126
+ connectionString += fmt .Sprintf ("%s=%s " , key , value )
127
+ delete (metadata , key )
128
+ } else {
129
+ connectionString += fmt .Sprintf ("%s=%s " , key , kv [1 ])
130
+ }
131
+ }
132
+ }
133
+ for k , v := range metadata {
134
+ connectionString += fmt .Sprintf ("%s=%s " , k , v )
135
+ }
136
+
137
+ if connectionString == "" {
138
+ return "" , errors .New ("failed to build connection string" )
139
+ }
140
+
141
+ return strings .TrimSpace (connectionString ), nil
142
+ }
143
+
144
+ func (m * PostgresAuthMetadata ) getConnectionStringMetadata () map [string ]string {
145
+ metadata := make (map [string ]string )
146
+ if m .User != "" {
147
+ metadata ["user" ] = m .User
148
+ }
149
+ if m .Host != "" {
150
+ metadata ["host" ] = m .Host
151
+ }
152
+ if m .HostAddr != "" {
153
+ metadata ["hostaddr" ] = m .HostAddr
154
+ }
155
+ if m .Port != "" {
156
+ metadata ["port" ] = m .Port
157
+ }
158
+ if m .Database != "" {
159
+ metadata ["database" ] = m .Database
160
+ }
161
+ if m .Password != "" {
162
+ metadata ["password" ] = m .Password
163
+ }
164
+ if m .SslRootCert != "" {
165
+ metadata ["sslrootcert" ] = m .SslRootCert
166
+ }
167
+ return metadata
168
+ }
169
+
170
+ func (m * PostgresAuthMetadata ) buildURLConnectionString (metadata map [string ]string ) (string , error ) {
171
+ u , err := url .Parse (m .ConnectionString )
172
+ if err != nil {
173
+ return "" , fmt .Errorf ("invalid URL connection string: %w" , err )
174
+ }
175
+
176
+ var username string
177
+ var password string
178
+ if u .User != nil {
179
+ username = u .User .Username ()
180
+ pw , set := u .User .Password ()
181
+ if set {
182
+ password = pw
183
+ }
184
+ }
185
+
186
+ if val , ok := metadata ["user" ]; ok {
187
+ username = val
188
+ }
189
+ if val , ok := metadata ["password" ]; ok {
190
+ password = val
191
+ }
192
+ if username != "" {
193
+ u .User = url .UserPassword (username , password )
194
+ }
195
+
196
+ if val , ok := metadata ["host" ]; ok {
197
+ u .Host = val
198
+ }
199
+ if val , ok := metadata ["hostaddr" ]; ok {
200
+ u .Host = val
201
+ }
202
+ if m .Port != "" {
203
+ u .Host = fmt .Sprintf ("%s:%s" , u .Host , m .Port )
204
+ }
205
+
206
+ if val , ok := metadata ["database" ]; ok {
207
+ u .Path = "/" + strings .TrimPrefix (val , "/" )
208
+ }
209
+
210
+ q := u .Query ()
211
+ if val , ok := metadata ["sslrootcert" ]; ok {
212
+ q .Set ("sslrootcert" , val )
213
+ }
214
+ u .RawQuery = q .Encode ()
215
+
216
+ return u .String (), nil
217
+ }
218
+
90
219
func (m * PostgresAuthMetadata ) BuildAwsIamOptions (logger logger.Logger , properties map [string ]string ) (* aws.Options , error ) {
91
220
awsRegion , _ := metadata .GetMetadataProperty (m .awsEnv .Metadata , "AWSRegion" )
92
221
region , _ := metadata .GetMetadataProperty (m .awsEnv .Metadata , "region" )
@@ -132,8 +261,11 @@ func (m *PostgresAuthMetadata) BuildAwsIamOptions(logger logger.Logger, properti
132
261
133
262
// GetPgxPoolConfig returns the pgxpool.Config object that contains the credentials for connecting to PostgreSQL.
134
263
func (m * PostgresAuthMetadata ) GetPgxPoolConfig () (* pgxpool.Config , error ) {
135
- // Get the config from the connection string
136
- config , err := pgxpool .ParseConfig (m .ConnectionString )
264
+ connectionString , err := m .buildConnectionString ()
265
+ if err != nil {
266
+ return nil , err
267
+ }
268
+ config , err := pgxpool .ParseConfig (connectionString )
137
269
if err != nil {
138
270
return nil , fmt .Errorf ("failed to parse connection string: %w" , err )
139
271
}
0 commit comments