1
1
//! Parse URL and convert to config struct.
2
- use std:: { collections:: BTreeSet , env:: var} ;
2
+ use std:: { collections:: BTreeSet , env:: var, str :: FromStr } ;
3
3
use url:: Url ;
4
4
5
- use super :: { ConfigAndUsers , Database , Error , User , Users } ;
5
+ use super :: { ConfigAndUsers , Database , Error , PoolerMode , Role , User , Users } ;
6
6
7
7
fn database_name ( url : & Url ) -> String {
8
8
let database = url. path ( ) . chars ( ) . skip ( 1 ) . collect :: < String > ( ) ;
@@ -21,12 +21,68 @@ impl From<&Url> for Database {
21
21
. unwrap_or ( "127.0.0.1" . into ( ) ) ;
22
22
let port = value. port ( ) . unwrap_or ( 5432 ) ;
23
23
24
- Database {
24
+ let mut database = Database {
25
25
name : database_name ( value) ,
26
26
host,
27
27
port,
28
28
..Default :: default ( )
29
+ } ;
30
+
31
+ for ( key, val) in value. query_pairs ( ) {
32
+ match key. as_ref ( ) {
33
+ "database_name" => database. database_name = Some ( val. to_string ( ) ) ,
34
+ "role" => {
35
+ if let Ok ( role) = Role :: from_str ( & val) {
36
+ database. role = role;
37
+ }
38
+ }
39
+ "shard" => {
40
+ if let Ok ( shard) = val. parse :: < usize > ( ) {
41
+ database. shard = shard;
42
+ }
43
+ }
44
+ "user" => database. user = Some ( val. to_string ( ) ) ,
45
+ "password" => database. password = Some ( val. to_string ( ) ) ,
46
+ "pool_size" => {
47
+ if let Ok ( size) = val. parse :: < usize > ( ) {
48
+ database. pool_size = Some ( size) ;
49
+ }
50
+ }
51
+ "min_pool_size" => {
52
+ if let Ok ( size) = val. parse :: < usize > ( ) {
53
+ database. min_pool_size = Some ( size) ;
54
+ }
55
+ }
56
+ "pooler_mode" => {
57
+ if let Ok ( mode) = PoolerMode :: from_str ( & val) {
58
+ database. pooler_mode = Some ( mode) ;
59
+ }
60
+ }
61
+ "statement_timeout" => {
62
+ if let Ok ( timeout) = val. parse :: < u64 > ( ) {
63
+ database. statement_timeout = Some ( timeout) ;
64
+ }
65
+ }
66
+ "idle_timeout" => {
67
+ if let Ok ( timeout) = val. parse :: < u64 > ( ) {
68
+ database. idle_timeout = Some ( timeout) ;
69
+ }
70
+ }
71
+ "read_only" => {
72
+ if let Ok ( read_only) = val. parse :: < bool > ( ) {
73
+ database. read_only = Some ( read_only) ;
74
+ }
75
+ }
76
+ "server_lifetime" => {
77
+ if let Ok ( lifetime) = val. parse :: < u64 > ( ) {
78
+ database. server_lifetime = Some ( lifetime) ;
79
+ }
80
+ }
81
+ _ => { }
82
+ }
29
83
}
84
+
85
+ database
30
86
}
31
87
}
32
88
@@ -39,7 +95,6 @@ impl From<&Url> for User {
39
95
user. to_string ( )
40
96
} ;
41
97
let password = value. password ( ) . unwrap_or ( "postgres" ) . to_owned ( ) ;
42
-
43
98
User {
44
99
name : user,
45
100
password : Some ( password) ,
@@ -74,6 +129,20 @@ impl ConfigAndUsers {
74
129
75
130
Ok ( self )
76
131
}
132
+
133
+ /// Load from mirroring strings.
134
+ pub fn mirroring_from_strings ( mut self , mirror_strs : & [ String ] ) -> Result < Self , Error > {
135
+ use super :: Mirroring ;
136
+
137
+ let mirroring = mirror_strs
138
+ . iter ( )
139
+ . map ( |s| Mirroring :: from_str ( s) . map_err ( |e| Error :: ParseError ( e) ) )
140
+ . collect :: < Result < Vec < _ > , _ > > ( ) ?;
141
+
142
+ self . config . mirroring = mirroring;
143
+
144
+ Ok ( self )
145
+ }
77
146
}
78
147
79
148
#[ cfg( test) ]
@@ -85,4 +154,151 @@ mod test {
85
154
let url = Url :: parse ( "postgres://user:password@host:5432/name" ) . unwrap ( ) ;
86
155
println ! ( "{:#?}" , url) ;
87
156
}
157
+
158
+ #[ test]
159
+ fn test_database_name_from_query_param ( ) {
160
+ let url =
161
+ Url :: parse ( "postgres://user:password@host:5432/name?database_name=dbname" ) . unwrap ( ) ;
162
+ let database = Database :: from ( & url) ;
163
+
164
+ assert_eq ! ( database. name, "name" ) ;
165
+ assert_eq ! ( database. database_name, Some ( "dbname" . to_string( ) ) ) ;
166
+ }
167
+
168
+ #[ test]
169
+ fn test_role_from_query_param ( ) {
170
+ let url = Url :: parse ( "postgres://user:password@host:5432/name?role=replica" ) . unwrap ( ) ;
171
+ let database = Database :: from ( & url) ;
172
+
173
+ assert_eq ! ( database. role, super :: super :: Role :: Replica ) ;
174
+ }
175
+
176
+ #[ test]
177
+ fn test_shard_from_query_param ( ) {
178
+ let url = Url :: parse ( "postgres://user:password@host:5432/name?shard=5" ) . unwrap ( ) ;
179
+ let database = Database :: from ( & url) ;
180
+
181
+ assert_eq ! ( database. shard, 5 ) ;
182
+ }
183
+
184
+ #[ test]
185
+ fn test_numeric_fields_from_query_params ( ) {
186
+ let url = Url :: parse ( "postgres://user:password@host:5432/name?pool_size=10&min_pool_size=2&statement_timeout=5000&idle_timeout=300&server_lifetime=3600" ) . unwrap ( ) ;
187
+ let database = Database :: from ( & url) ;
188
+
189
+ assert_eq ! ( database. pool_size, Some ( 10 ) ) ;
190
+ assert_eq ! ( database. min_pool_size, Some ( 2 ) ) ;
191
+ assert_eq ! ( database. statement_timeout, Some ( 5000 ) ) ;
192
+ assert_eq ! ( database. idle_timeout, Some ( 300 ) ) ;
193
+ assert_eq ! ( database. server_lifetime, Some ( 3600 ) ) ;
194
+ }
195
+
196
+ #[ test]
197
+ fn test_bool_field_from_query_param ( ) {
198
+ let url = Url :: parse ( "postgres://user:password@host:5432/name?read_only=true" ) . unwrap ( ) ;
199
+ let database = Database :: from ( & url) ;
200
+
201
+ assert_eq ! ( database. read_only, Some ( true ) ) ;
202
+ }
203
+
204
+ #[ test]
205
+ fn test_pooler_mode_from_query_param ( ) {
206
+ let url =
207
+ Url :: parse ( "postgres://user:password@host:5432/name?pooler_mode=session" ) . unwrap ( ) ;
208
+ let database = Database :: from ( & url) ;
209
+
210
+ assert_eq ! (
211
+ database. pooler_mode,
212
+ Some ( super :: super :: PoolerMode :: Session )
213
+ ) ;
214
+ }
215
+
216
+ #[ test]
217
+ fn test_string_fields_from_query_params ( ) {
218
+ let url = Url :: parse ( "postgres://user:password@host:5432/name?user=admin&password=secret" )
219
+ . unwrap ( ) ;
220
+ let database = Database :: from ( & url) ;
221
+
222
+ assert_eq ! ( database. user, Some ( "admin" . to_string( ) ) ) ;
223
+ assert_eq ! ( database. password, Some ( "secret" . to_string( ) ) ) ;
224
+ }
225
+
226
+ #[ test]
227
+ fn test_multiple_query_params ( ) {
228
+ let url = Url :: parse ( "postgres://user:password@host:5432/name?database_name=realdb&role=replica&shard=3&pool_size=20&read_only=true" ) . unwrap ( ) ;
229
+ let database = Database :: from ( & url) ;
230
+
231
+ assert_eq ! ( database. name, "name" ) ;
232
+ assert_eq ! ( database. database_name, Some ( "realdb" . to_string( ) ) ) ;
233
+ assert_eq ! ( database. role, super :: super :: Role :: Replica ) ;
234
+ assert_eq ! ( database. shard, 3 ) ;
235
+ assert_eq ! ( database. pool_size, Some ( 20 ) ) ;
236
+ assert_eq ! ( database. read_only, Some ( true ) ) ;
237
+ }
238
+
239
+ #[ test]
240
+ fn test_basic_mirroring_string ( ) {
241
+ let mirror_str = "source_db=primary&destination_db=backup" ;
242
+ let mirroring = super :: super :: Mirroring :: from_str ( mirror_str) . unwrap ( ) ;
243
+
244
+ assert_eq ! ( mirroring. source_db, "primary" ) ;
245
+ assert_eq ! ( mirroring. destination_db, "backup" ) ;
246
+ assert_eq ! ( mirroring. queue_length, None ) ;
247
+ assert_eq ! ( mirroring. exposure, None ) ;
248
+ }
249
+
250
+ #[ test]
251
+ fn test_mirroring_with_queue_length ( ) {
252
+ let mirror_str = "source_db=db1&destination_db=db2&queue_length=256" ;
253
+ let mirroring = super :: super :: Mirroring :: from_str ( mirror_str) . unwrap ( ) ;
254
+
255
+ assert_eq ! ( mirroring. source_db, "db1" ) ;
256
+ assert_eq ! ( mirroring. destination_db, "db2" ) ;
257
+ assert_eq ! ( mirroring. queue_length, Some ( 256 ) ) ;
258
+ assert_eq ! ( mirroring. exposure, None ) ;
259
+ }
260
+
261
+ #[ test]
262
+ fn test_mirroring_with_exposure ( ) {
263
+ let mirror_str = "source_db=prod&destination_db=staging&exposure=0.5" ;
264
+ let mirroring = super :: super :: Mirroring :: from_str ( mirror_str) . unwrap ( ) ;
265
+
266
+ assert_eq ! ( mirroring. source_db, "prod" ) ;
267
+ assert_eq ! ( mirroring. destination_db, "staging" ) ;
268
+ assert_eq ! ( mirroring. queue_length, None ) ;
269
+ assert_eq ! ( mirroring. exposure, Some ( 0.5 ) ) ;
270
+ }
271
+
272
+ #[ test]
273
+ fn test_mirroring_with_both_overrides ( ) {
274
+ let mirror_str = "source_db=main&destination_db=backup&queue_length=512&exposure=0.75" ;
275
+ let mirroring = super :: super :: Mirroring :: from_str ( mirror_str) . unwrap ( ) ;
276
+
277
+ assert_eq ! ( mirroring. source_db, "main" ) ;
278
+ assert_eq ! ( mirroring. destination_db, "backup" ) ;
279
+ assert_eq ! ( mirroring. queue_length, Some ( 512 ) ) ;
280
+ assert_eq ! ( mirroring. exposure, Some ( 0.75 ) ) ;
281
+ }
282
+
283
+ #[ test]
284
+ fn test_config_mirroring_from_strings ( ) {
285
+ let config = ConfigAndUsers :: default ( ) ;
286
+ let mirror_strs = vec ! [
287
+ "source_db=db1&destination_db=db1_mirror" . to_string( ) ,
288
+ "source_db=db2&destination_db=db2_mirror&queue_length=256&exposure=0.5" . to_string( ) ,
289
+ ] ;
290
+
291
+ let config = config. mirroring_from_strings ( & mirror_strs) . unwrap ( ) ;
292
+
293
+ assert_eq ! ( config. config. mirroring. len( ) , 2 ) ;
294
+ assert_eq ! ( config. config. mirroring[ 0 ] . source_db, "db1" ) ;
295
+ assert_eq ! ( config. config. mirroring[ 0 ] . destination_db, "db1_mirror" ) ;
296
+ assert_eq ! ( config. config. mirroring[ 0 ] . queue_length, None ) ;
297
+ assert_eq ! ( config. config. mirroring[ 0 ] . exposure, None ) ;
298
+
299
+ assert_eq ! ( config. config. mirroring[ 1 ] . source_db, "db2" ) ;
300
+ assert_eq ! ( config. config. mirroring[ 1 ] . destination_db, "db2_mirror" ) ;
301
+ assert_eq ! ( config. config. mirroring[ 1 ] . queue_length, Some ( 256 ) ) ;
302
+ assert_eq ! ( config. config. mirroring[ 1 ] . exposure, Some ( 0.5 ) ) ;
303
+ }
88
304
}
0 commit comments