1 package pgconn_test
2
3 import (
4 "context"
5 "crypto/tls"
6 "fmt"
7 "io/ioutil"
8 "os"
9 "os/user"
10 "runtime"
11 "strings"
12 "testing"
13 "time"
14
15 "github.com/jackc/pgconn"
16 "github.com/stretchr/testify/assert"
17 "github.com/stretchr/testify/require"
18 )
19
20 func TestParseConfig(t *testing.T) {
21 t.Parallel()
22
23 var osUserName string
24 osUser, err := user.Current()
25 if err == nil {
26
27
28 if runtime.GOOS == "windows" && strings.Contains(osUser.Username, "\\") {
29 osUserName = osUser.Username[strings.LastIndex(osUser.Username, "\\")+1:]
30 } else {
31 osUserName = osUser.Username
32 }
33 }
34
35 config, err := pgconn.ParseConfig("")
36 require.NoError(t, err)
37 defaultHost := config.Host
38
39 tests := []struct {
40 name string
41 connString string
42 config *pgconn.Config
43 }{
44
45 {
46 name: "sslmode not set (prefer)",
47 connString: "postgres://jack:secret@localhost:5432/mydb",
48 config: &pgconn.Config{
49 User: "jack",
50 Password: "secret",
51 Host: "localhost",
52 Port: 5432,
53 Database: "mydb",
54 TLSConfig: &tls.Config{
55 InsecureSkipVerify: true,
56 ServerName: "localhost",
57 },
58 RuntimeParams: map[string]string{},
59 Fallbacks: []*pgconn.FallbackConfig{
60 &pgconn.FallbackConfig{
61 Host: "localhost",
62 Port: 5432,
63 TLSConfig: nil,
64 },
65 },
66 },
67 },
68 {
69 name: "sslmode disable",
70 connString: "postgres://jack:secret@localhost:5432/mydb?sslmode=disable",
71 config: &pgconn.Config{
72 User: "jack",
73 Password: "secret",
74 Host: "localhost",
75 Port: 5432,
76 Database: "mydb",
77 TLSConfig: nil,
78 RuntimeParams: map[string]string{},
79 },
80 },
81 {
82 name: "sslmode allow",
83 connString: "postgres://jack:secret@localhost:5432/mydb?sslmode=allow",
84 config: &pgconn.Config{
85 User: "jack",
86 Password: "secret",
87 Host: "localhost",
88 Port: 5432,
89 Database: "mydb",
90 TLSConfig: nil,
91 RuntimeParams: map[string]string{},
92 Fallbacks: []*pgconn.FallbackConfig{
93 &pgconn.FallbackConfig{
94 Host: "localhost",
95 Port: 5432,
96 TLSConfig: &tls.Config{
97 InsecureSkipVerify: true,
98 ServerName: "localhost",
99 },
100 },
101 },
102 },
103 },
104 {
105 name: "sslmode prefer",
106 connString: "postgres://jack:secret@localhost:5432/mydb?sslmode=prefer",
107 config: &pgconn.Config{
108
109 User: "jack",
110 Password: "secret",
111 Host: "localhost",
112 Port: 5432,
113 Database: "mydb",
114 TLSConfig: &tls.Config{
115 InsecureSkipVerify: true,
116 ServerName: "localhost",
117 },
118 RuntimeParams: map[string]string{},
119 Fallbacks: []*pgconn.FallbackConfig{
120 &pgconn.FallbackConfig{
121 Host: "localhost",
122 Port: 5432,
123 TLSConfig: nil,
124 },
125 },
126 },
127 },
128 {
129 name: "sslmode require",
130 connString: "postgres://jack:secret@localhost:5432/mydb?sslmode=require",
131 config: &pgconn.Config{
132 User: "jack",
133 Password: "secret",
134 Host: "localhost",
135 Port: 5432,
136 Database: "mydb",
137 TLSConfig: &tls.Config{
138 InsecureSkipVerify: true,
139 ServerName: "localhost",
140 },
141 RuntimeParams: map[string]string{},
142 },
143 },
144 {
145 name: "sslmode verify-ca",
146 connString: "postgres://jack:secret@localhost:5432/mydb?sslmode=verify-ca",
147 config: &pgconn.Config{
148 User: "jack",
149 Password: "secret",
150 Host: "localhost",
151 Port: 5432,
152 Database: "mydb",
153 TLSConfig: &tls.Config{
154 InsecureSkipVerify: true,
155 ServerName: "localhost",
156 },
157 RuntimeParams: map[string]string{},
158 },
159 },
160 {
161 name: "sslmode verify-full",
162 connString: "postgres://jack:secret@localhost:5432/mydb?sslmode=verify-full",
163 config: &pgconn.Config{
164 User: "jack",
165 Password: "secret",
166 Host: "localhost",
167 Port: 5432,
168 Database: "mydb",
169 TLSConfig: &tls.Config{ServerName: "localhost"},
170 RuntimeParams: map[string]string{},
171 },
172 },
173 {
174 name: "database url everything",
175 connString: "postgres://jack:secret@localhost:5432/mydb?sslmode=disable&application_name=pgxtest&search_path=myschema&connect_timeout=5",
176 config: &pgconn.Config{
177 User: "jack",
178 Password: "secret",
179 Host: "localhost",
180 Port: 5432,
181 Database: "mydb",
182 TLSConfig: nil,
183 ConnectTimeout: 5 * time.Second,
184 RuntimeParams: map[string]string{
185 "application_name": "pgxtest",
186 "search_path": "myschema",
187 },
188 },
189 },
190 {
191 name: "database url missing password",
192 connString: "postgres://jack@localhost:5432/mydb?sslmode=disable",
193 config: &pgconn.Config{
194 User: "jack",
195 Host: "localhost",
196 Port: 5432,
197 Database: "mydb",
198 TLSConfig: nil,
199 RuntimeParams: map[string]string{},
200 },
201 },
202 {
203 name: "database url missing user and password",
204 connString: "postgres://localhost:5432/mydb?sslmode=disable",
205 config: &pgconn.Config{
206 User: osUserName,
207 Host: "localhost",
208 Port: 5432,
209 Database: "mydb",
210 TLSConfig: nil,
211 RuntimeParams: map[string]string{},
212 },
213 },
214 {
215 name: "database url missing port",
216 connString: "postgres://jack:secret@localhost:5432/mydb?sslmode=disable",
217 config: &pgconn.Config{
218 User: "jack",
219 Password: "secret",
220 Host: "localhost",
221 Port: 5432,
222 Database: "mydb",
223 TLSConfig: nil,
224 RuntimeParams: map[string]string{},
225 },
226 },
227 {
228 name: "database url unix domain socket host",
229 connString: "postgres:///foo?host=/tmp",
230 config: &pgconn.Config{
231 User: osUserName,
232 Host: "/tmp",
233 Port: 5432,
234 Database: "foo",
235 TLSConfig: nil,
236 RuntimeParams: map[string]string{},
237 },
238 },
239 {
240 name: "database url unix domain socket host on windows",
241 connString: "postgres:///foo?host=C:\\tmp",
242 config: &pgconn.Config{
243 User: osUserName,
244 Host: "C:\\tmp",
245 Port: 5432,
246 Database: "foo",
247 TLSConfig: nil,
248 RuntimeParams: map[string]string{},
249 },
250 },
251 {
252 name: "database url dbname",
253 connString: "postgres://localhost/?dbname=foo&sslmode=disable",
254 config: &pgconn.Config{
255 User: osUserName,
256 Host: "localhost",
257 Port: 5432,
258 Database: "foo",
259 TLSConfig: nil,
260 RuntimeParams: map[string]string{},
261 },
262 },
263 {
264 name: "database url postgresql protocol",
265 connString: "postgresql://jack@localhost:5432/mydb?sslmode=disable",
266 config: &pgconn.Config{
267 User: "jack",
268 Host: "localhost",
269 Port: 5432,
270 Database: "mydb",
271 TLSConfig: nil,
272 RuntimeParams: map[string]string{},
273 },
274 },
275 {
276 name: "database url IPv4 with port",
277 connString: "postgresql://jack@127.0.0.1:5433/mydb?sslmode=disable",
278 config: &pgconn.Config{
279 User: "jack",
280 Host: "127.0.0.1",
281 Port: 5433,
282 Database: "mydb",
283 TLSConfig: nil,
284 RuntimeParams: map[string]string{},
285 },
286 },
287 {
288 name: "database url IPv6 with port",
289 connString: "postgresql://jack@[2001:db8::1]:5433/mydb?sslmode=disable",
290 config: &pgconn.Config{
291 User: "jack",
292 Host: "2001:db8::1",
293 Port: 5433,
294 Database: "mydb",
295 TLSConfig: nil,
296 RuntimeParams: map[string]string{},
297 },
298 },
299 {
300 name: "database url IPv6 no port",
301 connString: "postgresql://jack@[2001:db8::1]/mydb?sslmode=disable",
302 config: &pgconn.Config{
303 User: "jack",
304 Host: "2001:db8::1",
305 Port: 5432,
306 Database: "mydb",
307 TLSConfig: nil,
308 RuntimeParams: map[string]string{},
309 },
310 },
311 {
312 name: "DSN everything",
313 connString: "user=jack password=secret host=localhost port=5432 dbname=mydb sslmode=disable application_name=pgxtest search_path=myschema connect_timeout=5",
314 config: &pgconn.Config{
315 User: "jack",
316 Password: "secret",
317 Host: "localhost",
318 Port: 5432,
319 Database: "mydb",
320 TLSConfig: nil,
321 ConnectTimeout: 5 * time.Second,
322 RuntimeParams: map[string]string{
323 "application_name": "pgxtest",
324 "search_path": "myschema",
325 },
326 },
327 },
328 {
329 name: "DSN with escaped single quote",
330 connString: "user=jack\\'s password=secret host=localhost port=5432 dbname=mydb sslmode=disable",
331 config: &pgconn.Config{
332 User: "jack's",
333 Password: "secret",
334 Host: "localhost",
335 Port: 5432,
336 Database: "mydb",
337 TLSConfig: nil,
338 RuntimeParams: map[string]string{},
339 },
340 },
341 {
342 name: "DSN with escaped backslash",
343 connString: "user=jack password=sooper\\\\secret host=localhost port=5432 dbname=mydb sslmode=disable",
344 config: &pgconn.Config{
345 User: "jack",
346 Password: "sooper\\secret",
347 Host: "localhost",
348 Port: 5432,
349 Database: "mydb",
350 TLSConfig: nil,
351 RuntimeParams: map[string]string{},
352 },
353 },
354 {
355 name: "DSN with single quoted values",
356 connString: "user='jack' host='localhost' dbname='mydb' sslmode='disable'",
357 config: &pgconn.Config{
358 User: "jack",
359 Host: "localhost",
360 Port: 5432,
361 Database: "mydb",
362 TLSConfig: nil,
363 RuntimeParams: map[string]string{},
364 },
365 },
366 {
367 name: "DSN with single quoted value with escaped single quote",
368 connString: "user='jack\\'s' host='localhost' dbname='mydb' sslmode='disable'",
369 config: &pgconn.Config{
370 User: "jack's",
371 Host: "localhost",
372 Port: 5432,
373 Database: "mydb",
374 TLSConfig: nil,
375 RuntimeParams: map[string]string{},
376 },
377 },
378 {
379 name: "DSN with empty single quoted value",
380 connString: "user='jack' password='' host='localhost' dbname='mydb' sslmode='disable'",
381 config: &pgconn.Config{
382 User: "jack",
383 Host: "localhost",
384 Port: 5432,
385 Database: "mydb",
386 TLSConfig: nil,
387 RuntimeParams: map[string]string{},
388 },
389 },
390 {
391 name: "DSN with space between key and value",
392 connString: "user = 'jack' password = '' host = 'localhost' dbname = 'mydb' sslmode='disable'",
393 config: &pgconn.Config{
394 User: "jack",
395 Host: "localhost",
396 Port: 5432,
397 Database: "mydb",
398 TLSConfig: nil,
399 RuntimeParams: map[string]string{},
400 },
401 },
402 {
403 name: "URL multiple hosts",
404 connString: "postgres://jack:secret@foo,bar,baz/mydb?sslmode=disable",
405 config: &pgconn.Config{
406 User: "jack",
407 Password: "secret",
408 Host: "foo",
409 Port: 5432,
410 Database: "mydb",
411 TLSConfig: nil,
412 RuntimeParams: map[string]string{},
413 Fallbacks: []*pgconn.FallbackConfig{
414 &pgconn.FallbackConfig{
415 Host: "bar",
416 Port: 5432,
417 TLSConfig: nil,
418 },
419 &pgconn.FallbackConfig{
420 Host: "baz",
421 Port: 5432,
422 TLSConfig: nil,
423 },
424 },
425 },
426 },
427 {
428 name: "URL multiple hosts and ports",
429 connString: "postgres://jack:secret@foo:1,bar:2,baz:3/mydb?sslmode=disable",
430 config: &pgconn.Config{
431 User: "jack",
432 Password: "secret",
433 Host: "foo",
434 Port: 1,
435 Database: "mydb",
436 TLSConfig: nil,
437 RuntimeParams: map[string]string{},
438 Fallbacks: []*pgconn.FallbackConfig{
439 &pgconn.FallbackConfig{
440 Host: "bar",
441 Port: 2,
442 TLSConfig: nil,
443 },
444 &pgconn.FallbackConfig{
445 Host: "baz",
446 Port: 3,
447 TLSConfig: nil,
448 },
449 },
450 },
451 },
452
453 {
454 name: "URL without host but with port still uses default host",
455 connString: "postgres://jack:secret@:1/mydb?sslmode=disable",
456 config: &pgconn.Config{
457 User: "jack",
458 Password: "secret",
459 Host: defaultHost,
460 Port: 1,
461 Database: "mydb",
462 TLSConfig: nil,
463 RuntimeParams: map[string]string{},
464 },
465 },
466 {
467 name: "DSN multiple hosts one port",
468 connString: "user=jack password=secret host=foo,bar,baz port=5432 dbname=mydb sslmode=disable",
469 config: &pgconn.Config{
470 User: "jack",
471 Password: "secret",
472 Host: "foo",
473 Port: 5432,
474 Database: "mydb",
475 TLSConfig: nil,
476 RuntimeParams: map[string]string{},
477 Fallbacks: []*pgconn.FallbackConfig{
478 &pgconn.FallbackConfig{
479 Host: "bar",
480 Port: 5432,
481 TLSConfig: nil,
482 },
483 &pgconn.FallbackConfig{
484 Host: "baz",
485 Port: 5432,
486 TLSConfig: nil,
487 },
488 },
489 },
490 },
491 {
492 name: "DSN multiple hosts multiple ports",
493 connString: "user=jack password=secret host=foo,bar,baz port=1,2,3 dbname=mydb sslmode=disable",
494 config: &pgconn.Config{
495 User: "jack",
496 Password: "secret",
497 Host: "foo",
498 Port: 1,
499 Database: "mydb",
500 TLSConfig: nil,
501 RuntimeParams: map[string]string{},
502 Fallbacks: []*pgconn.FallbackConfig{
503 &pgconn.FallbackConfig{
504 Host: "bar",
505 Port: 2,
506 TLSConfig: nil,
507 },
508 &pgconn.FallbackConfig{
509 Host: "baz",
510 Port: 3,
511 TLSConfig: nil,
512 },
513 },
514 },
515 },
516 {
517 name: "multiple hosts and fallback tsl",
518 connString: "user=jack password=secret host=foo,bar,baz dbname=mydb sslmode=prefer",
519 config: &pgconn.Config{
520 User: "jack",
521 Password: "secret",
522 Host: "foo",
523 Port: 5432,
524 Database: "mydb",
525 TLSConfig: &tls.Config{
526 InsecureSkipVerify: true,
527 ServerName: "foo",
528 },
529 RuntimeParams: map[string]string{},
530 Fallbacks: []*pgconn.FallbackConfig{
531 &pgconn.FallbackConfig{
532 Host: "foo",
533 Port: 5432,
534 TLSConfig: nil,
535 },
536 &pgconn.FallbackConfig{
537 Host: "bar",
538 Port: 5432,
539 TLSConfig: &tls.Config{
540 InsecureSkipVerify: true,
541 ServerName: "bar",
542 }},
543 &pgconn.FallbackConfig{
544 Host: "bar",
545 Port: 5432,
546 TLSConfig: nil,
547 },
548 &pgconn.FallbackConfig{
549 Host: "baz",
550 Port: 5432,
551 TLSConfig: &tls.Config{
552 InsecureSkipVerify: true,
553 ServerName: "baz",
554 }},
555 &pgconn.FallbackConfig{
556 Host: "baz",
557 Port: 5432,
558 TLSConfig: nil,
559 },
560 },
561 },
562 },
563 {
564 name: "target_session_attrs read-write",
565 connString: "postgres://jack:secret@localhost:5432/mydb?sslmode=disable&target_session_attrs=read-write",
566 config: &pgconn.Config{
567 User: "jack",
568 Password: "secret",
569 Host: "localhost",
570 Port: 5432,
571 Database: "mydb",
572 TLSConfig: nil,
573 RuntimeParams: map[string]string{},
574 ValidateConnect: pgconn.ValidateConnectTargetSessionAttrsReadWrite,
575 },
576 },
577 {
578 name: "target_session_attrs read-only",
579 connString: "postgres://jack:secret@localhost:5432/mydb?sslmode=disable&target_session_attrs=read-only",
580 config: &pgconn.Config{
581 User: "jack",
582 Password: "secret",
583 Host: "localhost",
584 Port: 5432,
585 Database: "mydb",
586 TLSConfig: nil,
587 RuntimeParams: map[string]string{},
588 ValidateConnect: pgconn.ValidateConnectTargetSessionAttrsReadOnly,
589 },
590 },
591 {
592 name: "target_session_attrs primary",
593 connString: "postgres://jack:secret@localhost:5432/mydb?sslmode=disable&target_session_attrs=primary",
594 config: &pgconn.Config{
595 User: "jack",
596 Password: "secret",
597 Host: "localhost",
598 Port: 5432,
599 Database: "mydb",
600 TLSConfig: nil,
601 RuntimeParams: map[string]string{},
602 ValidateConnect: pgconn.ValidateConnectTargetSessionAttrsPrimary,
603 },
604 },
605 {
606 name: "target_session_attrs standby",
607 connString: "postgres://jack:secret@localhost:5432/mydb?sslmode=disable&target_session_attrs=standby",
608 config: &pgconn.Config{
609 User: "jack",
610 Password: "secret",
611 Host: "localhost",
612 Port: 5432,
613 Database: "mydb",
614 TLSConfig: nil,
615 RuntimeParams: map[string]string{},
616 ValidateConnect: pgconn.ValidateConnectTargetSessionAttrsStandby,
617 },
618 },
619 {
620 name: "target_session_attrs prefer-standby",
621 connString: "postgres://jack:secret@localhost:5432/mydb?sslmode=disable&target_session_attrs=prefer-standby",
622 config: &pgconn.Config{
623 User: "jack",
624 Password: "secret",
625 Host: "localhost",
626 Port: 5432,
627 Database: "mydb",
628 TLSConfig: nil,
629 RuntimeParams: map[string]string{},
630 ValidateConnect: pgconn.ValidateConnectTargetSessionAttrsPreferStandby,
631 },
632 },
633 {
634 name: "target_session_attrs any",
635 connString: "postgres://jack:secret@localhost:5432/mydb?sslmode=disable&target_session_attrs=any",
636 config: &pgconn.Config{
637 User: "jack",
638 Password: "secret",
639 Host: "localhost",
640 Port: 5432,
641 Database: "mydb",
642 TLSConfig: nil,
643 RuntimeParams: map[string]string{},
644 },
645 },
646 {
647 name: "target_session_attrs not set (any)",
648 connString: "postgres://jack:secret@localhost:5432/mydb?sslmode=disable",
649 config: &pgconn.Config{
650 User: "jack",
651 Password: "secret",
652 Host: "localhost",
653 Port: 5432,
654 Database: "mydb",
655 TLSConfig: nil,
656 RuntimeParams: map[string]string{},
657 },
658 },
659 {
660 name: "SNI is set by default",
661 connString: "postgres://jack:secret@sni.test:5432/mydb?sslmode=require",
662 config: &pgconn.Config{
663 User: "jack",
664 Password: "secret",
665 Host: "sni.test",
666 Port: 5432,
667 Database: "mydb",
668 TLSConfig: &tls.Config{
669 InsecureSkipVerify: true,
670 ServerName: "sni.test",
671 },
672 RuntimeParams: map[string]string{},
673 },
674 },
675 {
676 name: "SNI is not set for IPv4",
677 connString: "postgres://jack:secret@1.1.1.1:5432/mydb?sslmode=require",
678 config: &pgconn.Config{
679 User: "jack",
680 Password: "secret",
681 Host: "1.1.1.1",
682 Port: 5432,
683 Database: "mydb",
684 TLSConfig: &tls.Config{
685 InsecureSkipVerify: true,
686 },
687 RuntimeParams: map[string]string{},
688 },
689 },
690 {
691 name: "SNI is not set for IPv6",
692 connString: "postgres://jack:secret@[::1]:5432/mydb?sslmode=require",
693 config: &pgconn.Config{
694 User: "jack",
695 Password: "secret",
696 Host: "::1",
697 Port: 5432,
698 Database: "mydb",
699 TLSConfig: &tls.Config{
700 InsecureSkipVerify: true,
701 },
702 RuntimeParams: map[string]string{},
703 },
704 },
705 {
706 name: "SNI is not set when disabled (URL-style)",
707 connString: "postgres://jack:secret@sni.test:5432/mydb?sslmode=require&sslsni=0",
708 config: &pgconn.Config{
709 User: "jack",
710 Password: "secret",
711 Host: "sni.test",
712 Port: 5432,
713 Database: "mydb",
714 TLSConfig: &tls.Config{
715 InsecureSkipVerify: true,
716 },
717 RuntimeParams: map[string]string{},
718 },
719 },
720 {
721 name: "SNI is not set when disabled (key/value style)",
722 connString: "user=jack password=secret host=sni.test dbname=mydb sslmode=require sslsni=0",
723 config: &pgconn.Config{
724 User: "jack",
725 Password: "secret",
726 Host: "sni.test",
727 Port: 5432,
728 Database: "mydb",
729 TLSConfig: &tls.Config{
730 InsecureSkipVerify: true,
731 },
732 RuntimeParams: map[string]string{},
733 },
734 },
735 }
736
737 for i, tt := range tests {
738 config, err := pgconn.ParseConfig(tt.connString)
739 if !assert.Nilf(t, err, "Test %d (%s)", i, tt.name) {
740 continue
741 }
742
743 assertConfigsEqual(t, tt.config, config, fmt.Sprintf("Test %d (%s)", i, tt.name))
744 }
745 }
746
747
748 func TestParseConfigDSNWithTrailingEmptyEqualDoesNotPanic(t *testing.T) {
749 _, err := pgconn.ParseConfig("host= user= password= port= database=")
750 require.NoError(t, err)
751 }
752
753 func TestParseConfigDSNLeadingEqual(t *testing.T) {
754 _, err := pgconn.ParseConfig("= user=jack")
755 require.Error(t, err)
756 }
757
758
759 func TestParseConfigDSNTrailingBackslash(t *testing.T) {
760 _, err := pgconn.ParseConfig(`x=x\`)
761 require.Error(t, err)
762 assert.Contains(t, err.Error(), "invalid backslash")
763 }
764
765 func TestConfigCopyReturnsEqualConfig(t *testing.T) {
766 connString := "postgres://jack:secret@localhost:5432/mydb?application_name=pgxtest&search_path=myschema&connect_timeout=5"
767 original, err := pgconn.ParseConfig(connString)
768 require.NoError(t, err)
769
770 copied := original.Copy()
771 assertConfigsEqual(t, original, copied, "Test Config.Copy() returns equal config")
772 }
773
774 func TestConfigCopyOriginalConfigDidNotChange(t *testing.T) {
775 connString := "postgres://jack:secret@localhost:5432/mydb?application_name=pgxtest&search_path=myschema&connect_timeout=5&sslmode=prefer"
776 original, err := pgconn.ParseConfig(connString)
777 require.NoError(t, err)
778
779 copied := original.Copy()
780 assertConfigsEqual(t, original, copied, "Test Config.Copy() returns equal config")
781
782 copied.Port = uint16(5433)
783 copied.RuntimeParams["foo"] = "bar"
784 copied.Fallbacks[0].Port = uint16(5433)
785
786 assert.Equal(t, uint16(5432), original.Port)
787 assert.Equal(t, "", original.RuntimeParams["foo"])
788 assert.Equal(t, uint16(5432), original.Fallbacks[0].Port)
789 }
790
791 func TestConfigCopyCanBeUsedToConnect(t *testing.T) {
792 connString := os.Getenv("PGX_TEST_CONN_STRING")
793 original, err := pgconn.ParseConfig(connString)
794 require.NoError(t, err)
795
796 copied := original.Copy()
797 assert.NotPanics(t, func() {
798 _, err = pgconn.ConnectConfig(context.Background(), copied)
799 })
800 assert.NoError(t, err)
801 }
802
803 func TestNetworkAddress(t *testing.T) {
804 tests := []struct {
805 name string
806 host string
807 wantNet string
808 }{
809 {
810 name: "Default Unix socket address",
811 host: "/var/run/postgresql",
812 wantNet: "unix",
813 },
814 {
815 name: "Windows Unix socket address (standard drive name)",
816 host: "C:\\tmp",
817 wantNet: "unix",
818 },
819 {
820 name: "Windows Unix socket address (first drive name)",
821 host: "A:\\tmp",
822 wantNet: "unix",
823 },
824 {
825 name: "Windows Unix socket address (last drive name)",
826 host: "Z:\\tmp",
827 wantNet: "unix",
828 },
829 {
830 name: "Assume TCP for unknown formats",
831 host: "a/tmp",
832 wantNet: "tcp",
833 },
834 {
835 name: "loopback interface",
836 host: "localhost",
837 wantNet: "tcp",
838 },
839 {
840 name: "IP address",
841 host: "127.0.0.1",
842 wantNet: "tcp",
843 },
844 }
845 for i, tt := range tests {
846 gotNet, _ := pgconn.NetworkAddress(tt.host, 5432)
847
848 assert.Equalf(t, tt.wantNet, gotNet, "Test %d (%s)", i, tt.name)
849 }
850 }
851
852 func assertConfigsEqual(t *testing.T, expected, actual *pgconn.Config, testName string) {
853 if !assert.NotNil(t, expected) {
854 return
855 }
856 if !assert.NotNil(t, actual) {
857 return
858 }
859
860 assert.Equalf(t, expected.Host, actual.Host, "%s - Host", testName)
861 assert.Equalf(t, expected.Database, actual.Database, "%s - Database", testName)
862 assert.Equalf(t, expected.Port, actual.Port, "%s - Port", testName)
863 assert.Equalf(t, expected.User, actual.User, "%s - User", testName)
864 assert.Equalf(t, expected.Password, actual.Password, "%s - Password", testName)
865 assert.Equalf(t, expected.ConnectTimeout, actual.ConnectTimeout, "%s - ConnectTimeout", testName)
866 assert.Equalf(t, expected.RuntimeParams, actual.RuntimeParams, "%s - RuntimeParams", testName)
867
868
869 assert.Equalf(t, expected.ValidateConnect == nil, actual.ValidateConnect == nil, "%s - ValidateConnect", testName)
870 assert.Equalf(t, expected.AfterConnect == nil, actual.AfterConnect == nil, "%s - AfterConnect", testName)
871
872 if assert.Equalf(t, expected.TLSConfig == nil, actual.TLSConfig == nil, "%s - TLSConfig", testName) {
873 if expected.TLSConfig != nil {
874 assert.Equalf(t, expected.TLSConfig.InsecureSkipVerify, actual.TLSConfig.InsecureSkipVerify, "%s - TLSConfig InsecureSkipVerify", testName)
875 assert.Equalf(t, expected.TLSConfig.ServerName, actual.TLSConfig.ServerName, "%s - TLSConfig ServerName", testName)
876 }
877 }
878
879 if assert.Equalf(t, len(expected.Fallbacks), len(actual.Fallbacks), "%s - Fallbacks", testName) {
880 for i := range expected.Fallbacks {
881 assert.Equalf(t, expected.Fallbacks[i].Host, actual.Fallbacks[i].Host, "%s - Fallback %d - Host", testName, i)
882 assert.Equalf(t, expected.Fallbacks[i].Port, actual.Fallbacks[i].Port, "%s - Fallback %d - Port", testName, i)
883
884 if assert.Equalf(t, expected.Fallbacks[i].TLSConfig == nil, actual.Fallbacks[i].TLSConfig == nil, "%s - Fallback %d - TLSConfig", testName, i) {
885 if expected.Fallbacks[i].TLSConfig != nil {
886 assert.Equalf(t, expected.Fallbacks[i].TLSConfig.InsecureSkipVerify, actual.Fallbacks[i].TLSConfig.InsecureSkipVerify, "%s - Fallback %d - TLSConfig InsecureSkipVerify", testName)
887 assert.Equalf(t, expected.Fallbacks[i].TLSConfig.ServerName, actual.Fallbacks[i].TLSConfig.ServerName, "%s - Fallback %d - TLSConfig ServerName", testName)
888 }
889 }
890 }
891 }
892 }
893
894 func TestParseConfigEnvLibpq(t *testing.T) {
895 var osUserName string
896 osUser, err := user.Current()
897 if err == nil {
898
899
900 if runtime.GOOS == "windows" && strings.Contains(osUser.Username, "\\") {
901 osUserName = osUser.Username[strings.LastIndex(osUser.Username, "\\")+1:]
902 } else {
903 osUserName = osUser.Username
904 }
905 }
906
907 pgEnvvars := []string{"PGHOST", "PGPORT", "PGDATABASE", "PGUSER", "PGPASSWORD", "PGAPPNAME", "PGSSLMODE", "PGCONNECT_TIMEOUT", "PGSSLSNI"}
908
909 savedEnv := make(map[string]string)
910 for _, n := range pgEnvvars {
911 savedEnv[n] = os.Getenv(n)
912 }
913 defer func() {
914 for k, v := range savedEnv {
915 err := os.Setenv(k, v)
916 if err != nil {
917 t.Fatalf("Unable to restore environment: %v", err)
918 }
919 }
920 }()
921
922 tests := []struct {
923 name string
924 envvars map[string]string
925 config *pgconn.Config
926 }{
927 {
928
929 name: "PGHOST only",
930 envvars: map[string]string{"PGHOST": "123.123.123.123"},
931 config: &pgconn.Config{
932 User: osUserName,
933 Host: "123.123.123.123",
934 Port: 5432,
935 TLSConfig: &tls.Config{
936 InsecureSkipVerify: true,
937 },
938 RuntimeParams: map[string]string{},
939 Fallbacks: []*pgconn.FallbackConfig{
940 &pgconn.FallbackConfig{
941 Host: "123.123.123.123",
942 Port: 5432,
943 TLSConfig: nil,
944 },
945 },
946 },
947 },
948 {
949 name: "All non-TLS environment",
950 envvars: map[string]string{
951 "PGHOST": "123.123.123.123",
952 "PGPORT": "7777",
953 "PGDATABASE": "foo",
954 "PGUSER": "bar",
955 "PGPASSWORD": "baz",
956 "PGCONNECT_TIMEOUT": "10",
957 "PGSSLMODE": "disable",
958 "PGAPPNAME": "pgxtest",
959 },
960 config: &pgconn.Config{
961 Host: "123.123.123.123",
962 Port: 7777,
963 Database: "foo",
964 User: "bar",
965 Password: "baz",
966 ConnectTimeout: 10 * time.Second,
967 TLSConfig: nil,
968 RuntimeParams: map[string]string{"application_name": "pgxtest"},
969 },
970 },
971 {
972 name: "SNI can be disabled via environment variable",
973 envvars: map[string]string{
974 "PGHOST": "test.foo",
975 "PGSSLMODE": "require",
976 "PGSSLSNI": "0",
977 },
978 config: &pgconn.Config{
979 User: osUserName,
980 Host: "test.foo",
981 Port: 5432,
982 TLSConfig: &tls.Config{
983 InsecureSkipVerify: true,
984 },
985 RuntimeParams: map[string]string{},
986 },
987 },
988 }
989
990 for i, tt := range tests {
991 for _, n := range pgEnvvars {
992 err := os.Unsetenv(n)
993 require.NoError(t, err)
994 }
995
996 for k, v := range tt.envvars {
997 err := os.Setenv(k, v)
998 require.NoError(t, err)
999 }
1000
1001 config, err := pgconn.ParseConfig("")
1002 if !assert.Nilf(t, err, "Test %d (%s)", i, tt.name) {
1003 continue
1004 }
1005
1006 assertConfigsEqual(t, tt.config, config, fmt.Sprintf("Test %d (%s)", i, tt.name))
1007 }
1008 }
1009
1010 func TestParseConfigReadsPgPassfile(t *testing.T) {
1011 t.Parallel()
1012
1013 tf, err := ioutil.TempFile("", "")
1014 require.NoError(t, err)
1015
1016 defer tf.Close()
1017 defer os.Remove(tf.Name())
1018
1019 _, err = tf.Write([]byte("test1:5432:curlydb:curly:nyuknyuknyuk"))
1020 require.NoError(t, err)
1021
1022 connString := fmt.Sprintf("postgres://curly@test1:5432/curlydb?sslmode=disable&passfile=%s", tf.Name())
1023 expected := &pgconn.Config{
1024 User: "curly",
1025 Password: "nyuknyuknyuk",
1026 Host: "test1",
1027 Port: 5432,
1028 Database: "curlydb",
1029 TLSConfig: nil,
1030 RuntimeParams: map[string]string{},
1031 }
1032
1033 actual, err := pgconn.ParseConfig(connString)
1034 assert.NoError(t, err)
1035
1036 assertConfigsEqual(t, expected, actual, "passfile")
1037 }
1038
1039 func TestParseConfigReadsPgServiceFile(t *testing.T) {
1040 t.Parallel()
1041
1042 tf, err := ioutil.TempFile("", "")
1043 require.NoError(t, err)
1044
1045 defer tf.Close()
1046 defer os.Remove(tf.Name())
1047
1048 _, err = tf.Write([]byte(`
1049 [abc]
1050 host=abc.example.com
1051 port=9999
1052 dbname=abcdb
1053 user=abcuser
1054
1055 [def]
1056 host = def.example.com
1057 dbname = defdb
1058 user = defuser
1059 application_name = spaced string
1060 `))
1061 require.NoError(t, err)
1062
1063 tests := []struct {
1064 name string
1065 connString string
1066 config *pgconn.Config
1067 }{
1068 {
1069 name: "abc",
1070 connString: fmt.Sprintf("postgres:///?servicefile=%s&service=%s", tf.Name(), "abc"),
1071 config: &pgconn.Config{
1072 Host: "abc.example.com",
1073 Database: "abcdb",
1074 User: "abcuser",
1075 Port: 9999,
1076 TLSConfig: &tls.Config{
1077 InsecureSkipVerify: true,
1078 ServerName: "abc.example.com",
1079 },
1080 RuntimeParams: map[string]string{},
1081 Fallbacks: []*pgconn.FallbackConfig{
1082 &pgconn.FallbackConfig{
1083 Host: "abc.example.com",
1084 Port: 9999,
1085 TLSConfig: nil,
1086 },
1087 },
1088 },
1089 },
1090 {
1091 name: "def",
1092 connString: fmt.Sprintf("postgres:///?servicefile=%s&service=%s", tf.Name(), "def"),
1093 config: &pgconn.Config{
1094 Host: "def.example.com",
1095 Port: 5432,
1096 Database: "defdb",
1097 User: "defuser",
1098 TLSConfig: &tls.Config{
1099 InsecureSkipVerify: true,
1100 ServerName: "def.example.com",
1101 },
1102 RuntimeParams: map[string]string{"application_name": "spaced string"},
1103 Fallbacks: []*pgconn.FallbackConfig{
1104 &pgconn.FallbackConfig{
1105 Host: "def.example.com",
1106 Port: 5432,
1107 TLSConfig: nil,
1108 },
1109 },
1110 },
1111 },
1112 {
1113 name: "conn string has precedence",
1114 connString: fmt.Sprintf("postgres://other.example.com:7777/?servicefile=%s&service=%s&sslmode=disable", tf.Name(), "abc"),
1115 config: &pgconn.Config{
1116 Host: "other.example.com",
1117 Database: "abcdb",
1118 User: "abcuser",
1119 Port: 7777,
1120 TLSConfig: nil,
1121 RuntimeParams: map[string]string{},
1122 },
1123 },
1124 }
1125
1126 for i, tt := range tests {
1127 config, err := pgconn.ParseConfig(tt.connString)
1128 if !assert.NoErrorf(t, err, "Test %d (%s)", i, tt.name) {
1129 continue
1130 }
1131
1132 assertConfigsEqual(t, tt.config, config, fmt.Sprintf("Test %d (%s)", i, tt.name))
1133 }
1134 }
1135
1136 func TestParseConfigExtractsMinReadBufferSize(t *testing.T) {
1137 t.Parallel()
1138
1139 config, err := pgconn.ParseConfig("min_read_buffer_size=0")
1140 require.NoError(t, err)
1141 _, present := config.RuntimeParams["min_read_buffer_size"]
1142 require.False(t, present)
1143
1144
1145
1146 }
1147
View as plain text