1package pgconn_test 2 3import ( 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 20func TestParseConfig(t *testing.T) { 21 t.Parallel() 22 23 var osUserName string 24 osUser, err := user.Current() 25 if err == nil { 26 // Windows gives us the username here as `DOMAIN\user` or `LOCALPCNAME\user`, 27 // but the libpq default is just the `user` portion, so we strip off the first part. 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 // Test all sslmodes 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 }, 57 RuntimeParams: map[string]string{}, 58 Fallbacks: []*pgconn.FallbackConfig{ 59 &pgconn.FallbackConfig{ 60 Host: "localhost", 61 Port: 5432, 62 TLSConfig: nil, 63 }, 64 }, 65 }, 66 }, 67 { 68 name: "sslmode disable", 69 connString: "postgres://jack:secret@localhost:5432/mydb?sslmode=disable", 70 config: &pgconn.Config{ 71 User: "jack", 72 Password: "secret", 73 Host: "localhost", 74 Port: 5432, 75 Database: "mydb", 76 TLSConfig: nil, 77 RuntimeParams: map[string]string{}, 78 }, 79 }, 80 { 81 name: "sslmode allow", 82 connString: "postgres://jack:secret@localhost:5432/mydb?sslmode=allow", 83 config: &pgconn.Config{ 84 User: "jack", 85 Password: "secret", 86 Host: "localhost", 87 Port: 5432, 88 Database: "mydb", 89 TLSConfig: nil, 90 RuntimeParams: map[string]string{}, 91 Fallbacks: []*pgconn.FallbackConfig{ 92 &pgconn.FallbackConfig{ 93 Host: "localhost", 94 Port: 5432, 95 TLSConfig: &tls.Config{ 96 InsecureSkipVerify: true, 97 }, 98 }, 99 }, 100 }, 101 }, 102 { 103 name: "sslmode prefer", 104 connString: "postgres://jack:secret@localhost:5432/mydb?sslmode=prefer", 105 config: &pgconn.Config{ 106 107 User: "jack", 108 Password: "secret", 109 Host: "localhost", 110 Port: 5432, 111 Database: "mydb", 112 TLSConfig: &tls.Config{ 113 InsecureSkipVerify: true, 114 }, 115 RuntimeParams: map[string]string{}, 116 Fallbacks: []*pgconn.FallbackConfig{ 117 &pgconn.FallbackConfig{ 118 Host: "localhost", 119 Port: 5432, 120 TLSConfig: nil, 121 }, 122 }, 123 }, 124 }, 125 { 126 name: "sslmode require", 127 connString: "postgres://jack:secret@localhost:5432/mydb?sslmode=require", 128 config: &pgconn.Config{ 129 User: "jack", 130 Password: "secret", 131 Host: "localhost", 132 Port: 5432, 133 Database: "mydb", 134 TLSConfig: &tls.Config{ 135 InsecureSkipVerify: true, 136 }, 137 RuntimeParams: map[string]string{}, 138 }, 139 }, 140 { 141 name: "sslmode verify-ca", 142 connString: "postgres://jack:secret@localhost:5432/mydb?sslmode=verify-ca", 143 config: &pgconn.Config{ 144 User: "jack", 145 Password: "secret", 146 Host: "localhost", 147 Port: 5432, 148 Database: "mydb", 149 TLSConfig: &tls.Config{ 150 InsecureSkipVerify: true, 151 }, 152 RuntimeParams: map[string]string{}, 153 }, 154 }, 155 { 156 name: "sslmode verify-full", 157 connString: "postgres://jack:secret@localhost:5432/mydb?sslmode=verify-full", 158 config: &pgconn.Config{ 159 User: "jack", 160 Password: "secret", 161 Host: "localhost", 162 Port: 5432, 163 Database: "mydb", 164 TLSConfig: &tls.Config{ServerName: "localhost"}, 165 RuntimeParams: map[string]string{}, 166 }, 167 }, 168 { 169 name: "database url everything", 170 connString: "postgres://jack:secret@localhost:5432/mydb?sslmode=disable&application_name=pgxtest&search_path=myschema&connect_timeout=5", 171 config: &pgconn.Config{ 172 User: "jack", 173 Password: "secret", 174 Host: "localhost", 175 Port: 5432, 176 Database: "mydb", 177 TLSConfig: nil, 178 ConnectTimeout: 5 * time.Second, 179 RuntimeParams: map[string]string{ 180 "application_name": "pgxtest", 181 "search_path": "myschema", 182 }, 183 }, 184 }, 185 { 186 name: "database url missing password", 187 connString: "postgres://jack@localhost:5432/mydb?sslmode=disable", 188 config: &pgconn.Config{ 189 User: "jack", 190 Host: "localhost", 191 Port: 5432, 192 Database: "mydb", 193 TLSConfig: nil, 194 RuntimeParams: map[string]string{}, 195 }, 196 }, 197 { 198 name: "database url missing user and password", 199 connString: "postgres://localhost:5432/mydb?sslmode=disable", 200 config: &pgconn.Config{ 201 User: osUserName, 202 Host: "localhost", 203 Port: 5432, 204 Database: "mydb", 205 TLSConfig: nil, 206 RuntimeParams: map[string]string{}, 207 }, 208 }, 209 { 210 name: "database url missing port", 211 connString: "postgres://jack:secret@localhost:5432/mydb?sslmode=disable", 212 config: &pgconn.Config{ 213 User: "jack", 214 Password: "secret", 215 Host: "localhost", 216 Port: 5432, 217 Database: "mydb", 218 TLSConfig: nil, 219 RuntimeParams: map[string]string{}, 220 }, 221 }, 222 { 223 name: "database url unix domain socket host", 224 connString: "postgres:///foo?host=/tmp", 225 config: &pgconn.Config{ 226 User: osUserName, 227 Host: "/tmp", 228 Port: 5432, 229 Database: "foo", 230 TLSConfig: nil, 231 RuntimeParams: map[string]string{}, 232 }, 233 }, 234 { 235 name: "database url dbname", 236 connString: "postgres://localhost/?dbname=foo&sslmode=disable", 237 config: &pgconn.Config{ 238 User: osUserName, 239 Host: "localhost", 240 Port: 5432, 241 Database: "foo", 242 TLSConfig: nil, 243 RuntimeParams: map[string]string{}, 244 }, 245 }, 246 { 247 name: "database url postgresql protocol", 248 connString: "postgresql://jack@localhost:5432/mydb?sslmode=disable", 249 config: &pgconn.Config{ 250 User: "jack", 251 Host: "localhost", 252 Port: 5432, 253 Database: "mydb", 254 TLSConfig: nil, 255 RuntimeParams: map[string]string{}, 256 }, 257 }, 258 { 259 name: "database url IPv4 with port", 260 connString: "postgresql://jack@127.0.0.1:5433/mydb?sslmode=disable", 261 config: &pgconn.Config{ 262 User: "jack", 263 Host: "127.0.0.1", 264 Port: 5433, 265 Database: "mydb", 266 TLSConfig: nil, 267 RuntimeParams: map[string]string{}, 268 }, 269 }, 270 { 271 name: "database url IPv6 with port", 272 connString: "postgresql://jack@[2001:db8::1]:5433/mydb?sslmode=disable", 273 config: &pgconn.Config{ 274 User: "jack", 275 Host: "2001:db8::1", 276 Port: 5433, 277 Database: "mydb", 278 TLSConfig: nil, 279 RuntimeParams: map[string]string{}, 280 }, 281 }, 282 { 283 name: "database url IPv6 no port", 284 connString: "postgresql://jack@[2001:db8::1]/mydb?sslmode=disable", 285 config: &pgconn.Config{ 286 User: "jack", 287 Host: "2001:db8::1", 288 Port: 5432, 289 Database: "mydb", 290 TLSConfig: nil, 291 RuntimeParams: map[string]string{}, 292 }, 293 }, 294 { 295 name: "DSN everything", 296 connString: "user=jack password=secret host=localhost port=5432 dbname=mydb sslmode=disable application_name=pgxtest search_path=myschema connect_timeout=5", 297 config: &pgconn.Config{ 298 User: "jack", 299 Password: "secret", 300 Host: "localhost", 301 Port: 5432, 302 Database: "mydb", 303 TLSConfig: nil, 304 ConnectTimeout: 5 * time.Second, 305 RuntimeParams: map[string]string{ 306 "application_name": "pgxtest", 307 "search_path": "myschema", 308 }, 309 }, 310 }, 311 { 312 name: "DSN with escaped single quote", 313 connString: "user=jack\\'s password=secret host=localhost port=5432 dbname=mydb sslmode=disable", 314 config: &pgconn.Config{ 315 User: "jack's", 316 Password: "secret", 317 Host: "localhost", 318 Port: 5432, 319 Database: "mydb", 320 TLSConfig: nil, 321 RuntimeParams: map[string]string{}, 322 }, 323 }, 324 { 325 name: "DSN with escaped backslash", 326 connString: "user=jack password=sooper\\\\secret host=localhost port=5432 dbname=mydb sslmode=disable", 327 config: &pgconn.Config{ 328 User: "jack", 329 Password: "sooper\\secret", 330 Host: "localhost", 331 Port: 5432, 332 Database: "mydb", 333 TLSConfig: nil, 334 RuntimeParams: map[string]string{}, 335 }, 336 }, 337 { 338 name: "DSN with single quoted values", 339 connString: "user='jack' host='localhost' dbname='mydb' sslmode='disable'", 340 config: &pgconn.Config{ 341 User: "jack", 342 Host: "localhost", 343 Port: 5432, 344 Database: "mydb", 345 TLSConfig: nil, 346 RuntimeParams: map[string]string{}, 347 }, 348 }, 349 { 350 name: "DSN with single quoted value with escaped single quote", 351 connString: "user='jack\\'s' host='localhost' dbname='mydb' sslmode='disable'", 352 config: &pgconn.Config{ 353 User: "jack's", 354 Host: "localhost", 355 Port: 5432, 356 Database: "mydb", 357 TLSConfig: nil, 358 RuntimeParams: map[string]string{}, 359 }, 360 }, 361 { 362 name: "DSN with empty single quoted value", 363 connString: "user='jack' password='' host='localhost' dbname='mydb' sslmode='disable'", 364 config: &pgconn.Config{ 365 User: "jack", 366 Host: "localhost", 367 Port: 5432, 368 Database: "mydb", 369 TLSConfig: nil, 370 RuntimeParams: map[string]string{}, 371 }, 372 }, 373 { 374 name: "DSN with space between key and value", 375 connString: "user = 'jack' password = '' host = 'localhost' dbname = 'mydb' sslmode='disable'", 376 config: &pgconn.Config{ 377 User: "jack", 378 Host: "localhost", 379 Port: 5432, 380 Database: "mydb", 381 TLSConfig: nil, 382 RuntimeParams: map[string]string{}, 383 }, 384 }, 385 { 386 name: "URL multiple hosts", 387 connString: "postgres://jack:secret@foo,bar,baz/mydb?sslmode=disable", 388 config: &pgconn.Config{ 389 User: "jack", 390 Password: "secret", 391 Host: "foo", 392 Port: 5432, 393 Database: "mydb", 394 TLSConfig: nil, 395 RuntimeParams: map[string]string{}, 396 Fallbacks: []*pgconn.FallbackConfig{ 397 &pgconn.FallbackConfig{ 398 Host: "bar", 399 Port: 5432, 400 TLSConfig: nil, 401 }, 402 &pgconn.FallbackConfig{ 403 Host: "baz", 404 Port: 5432, 405 TLSConfig: nil, 406 }, 407 }, 408 }, 409 }, 410 { 411 name: "URL multiple hosts and ports", 412 connString: "postgres://jack:secret@foo:1,bar:2,baz:3/mydb?sslmode=disable", 413 config: &pgconn.Config{ 414 User: "jack", 415 Password: "secret", 416 Host: "foo", 417 Port: 1, 418 Database: "mydb", 419 TLSConfig: nil, 420 RuntimeParams: map[string]string{}, 421 Fallbacks: []*pgconn.FallbackConfig{ 422 &pgconn.FallbackConfig{ 423 Host: "bar", 424 Port: 2, 425 TLSConfig: nil, 426 }, 427 &pgconn.FallbackConfig{ 428 Host: "baz", 429 Port: 3, 430 TLSConfig: nil, 431 }, 432 }, 433 }, 434 }, 435 // https://github.com/jackc/pgconn/issues/72 436 { 437 name: "URL without host but with port still uses default host", 438 connString: "postgres://jack:secret@:1/mydb?sslmode=disable", 439 config: &pgconn.Config{ 440 User: "jack", 441 Password: "secret", 442 Host: defaultHost, 443 Port: 1, 444 Database: "mydb", 445 TLSConfig: nil, 446 RuntimeParams: map[string]string{}, 447 }, 448 }, 449 { 450 name: "DSN multiple hosts one port", 451 connString: "user=jack password=secret host=foo,bar,baz port=5432 dbname=mydb sslmode=disable", 452 config: &pgconn.Config{ 453 User: "jack", 454 Password: "secret", 455 Host: "foo", 456 Port: 5432, 457 Database: "mydb", 458 TLSConfig: nil, 459 RuntimeParams: map[string]string{}, 460 Fallbacks: []*pgconn.FallbackConfig{ 461 &pgconn.FallbackConfig{ 462 Host: "bar", 463 Port: 5432, 464 TLSConfig: nil, 465 }, 466 &pgconn.FallbackConfig{ 467 Host: "baz", 468 Port: 5432, 469 TLSConfig: nil, 470 }, 471 }, 472 }, 473 }, 474 { 475 name: "DSN multiple hosts multiple ports", 476 connString: "user=jack password=secret host=foo,bar,baz port=1,2,3 dbname=mydb sslmode=disable", 477 config: &pgconn.Config{ 478 User: "jack", 479 Password: "secret", 480 Host: "foo", 481 Port: 1, 482 Database: "mydb", 483 TLSConfig: nil, 484 RuntimeParams: map[string]string{}, 485 Fallbacks: []*pgconn.FallbackConfig{ 486 &pgconn.FallbackConfig{ 487 Host: "bar", 488 Port: 2, 489 TLSConfig: nil, 490 }, 491 &pgconn.FallbackConfig{ 492 Host: "baz", 493 Port: 3, 494 TLSConfig: nil, 495 }, 496 }, 497 }, 498 }, 499 { 500 name: "multiple hosts and fallback tsl", 501 connString: "user=jack password=secret host=foo,bar,baz dbname=mydb sslmode=prefer", 502 config: &pgconn.Config{ 503 User: "jack", 504 Password: "secret", 505 Host: "foo", 506 Port: 5432, 507 Database: "mydb", 508 TLSConfig: &tls.Config{ 509 InsecureSkipVerify: true, 510 }, 511 RuntimeParams: map[string]string{}, 512 Fallbacks: []*pgconn.FallbackConfig{ 513 &pgconn.FallbackConfig{ 514 Host: "foo", 515 Port: 5432, 516 TLSConfig: nil, 517 }, 518 &pgconn.FallbackConfig{ 519 Host: "bar", 520 Port: 5432, 521 TLSConfig: &tls.Config{ 522 InsecureSkipVerify: true, 523 }}, 524 &pgconn.FallbackConfig{ 525 Host: "bar", 526 Port: 5432, 527 TLSConfig: nil, 528 }, 529 &pgconn.FallbackConfig{ 530 Host: "baz", 531 Port: 5432, 532 TLSConfig: &tls.Config{ 533 InsecureSkipVerify: true, 534 }}, 535 &pgconn.FallbackConfig{ 536 Host: "baz", 537 Port: 5432, 538 TLSConfig: nil, 539 }, 540 }, 541 }, 542 }, 543 { 544 name: "target_session_attrs", 545 connString: "postgres://jack:secret@localhost:5432/mydb?sslmode=disable&target_session_attrs=read-write", 546 config: &pgconn.Config{ 547 User: "jack", 548 Password: "secret", 549 Host: "localhost", 550 Port: 5432, 551 Database: "mydb", 552 TLSConfig: nil, 553 RuntimeParams: map[string]string{}, 554 ValidateConnect: pgconn.ValidateConnectTargetSessionAttrsReadWrite, 555 }, 556 }, 557 } 558 559 for i, tt := range tests { 560 config, err := pgconn.ParseConfig(tt.connString) 561 if !assert.Nilf(t, err, "Test %d (%s)", i, tt.name) { 562 continue 563 } 564 565 assertConfigsEqual(t, tt.config, config, fmt.Sprintf("Test %d (%s)", i, tt.name)) 566 } 567} 568 569// https://github.com/jackc/pgconn/issues/47 570func TestParseConfigDSNWithTrailingEmptyEqualDoesNotPanic(t *testing.T) { 571 _, err := pgconn.ParseConfig("host= user= password= port= database=") 572 require.NoError(t, err) 573} 574 575func TestParseConfigDSNLeadingEqual(t *testing.T) { 576 _, err := pgconn.ParseConfig("= user=jack") 577 require.Error(t, err) 578} 579 580// https://github.com/jackc/pgconn/issues/49 581func TestParseConfigDSNTrailingBackslash(t *testing.T) { 582 _, err := pgconn.ParseConfig(`x=x\`) 583 require.Error(t, err) 584 assert.Contains(t, err.Error(), "invalid backslash") 585} 586 587func TestConfigCopyReturnsEqualConfig(t *testing.T) { 588 connString := "postgres://jack:secret@localhost:5432/mydb?application_name=pgxtest&search_path=myschema&connect_timeout=5" 589 original, err := pgconn.ParseConfig(connString) 590 require.NoError(t, err) 591 592 copied := original.Copy() 593 assertConfigsEqual(t, original, copied, "Test Config.Copy() returns equal config") 594} 595 596func TestConfigCopyOriginalConfigDidNotChange(t *testing.T) { 597 connString := "postgres://jack:secret@localhost:5432/mydb?application_name=pgxtest&search_path=myschema&connect_timeout=5&sslmode=prefer" 598 original, err := pgconn.ParseConfig(connString) 599 require.NoError(t, err) 600 601 copied := original.Copy() 602 assertConfigsEqual(t, original, copied, "Test Config.Copy() returns equal config") 603 604 copied.Port = uint16(5433) 605 copied.RuntimeParams["foo"] = "bar" 606 copied.Fallbacks[0].Port = uint16(5433) 607 608 assert.Equal(t, uint16(5432), original.Port) 609 assert.Equal(t, "", original.RuntimeParams["foo"]) 610 assert.Equal(t, uint16(5432), original.Fallbacks[0].Port) 611} 612 613func TestConfigCopyCanBeUsedToConnect(t *testing.T) { 614 connString := os.Getenv("PGX_TEST_CONN_STRING") 615 original, err := pgconn.ParseConfig(connString) 616 require.NoError(t, err) 617 618 copied := original.Copy() 619 assert.NotPanics(t, func() { 620 _, err = pgconn.ConnectConfig(context.Background(), copied) 621 }) 622 assert.NoError(t, err) 623} 624 625func assertConfigsEqual(t *testing.T, expected, actual *pgconn.Config, testName string) { 626 if !assert.NotNil(t, expected) { 627 return 628 } 629 if !assert.NotNil(t, actual) { 630 return 631 } 632 633 assert.Equalf(t, expected.Host, actual.Host, "%s - Host", testName) 634 assert.Equalf(t, expected.Database, actual.Database, "%s - Database", testName) 635 assert.Equalf(t, expected.Port, actual.Port, "%s - Port", testName) 636 assert.Equalf(t, expected.User, actual.User, "%s - User", testName) 637 assert.Equalf(t, expected.Password, actual.Password, "%s - Password", testName) 638 assert.Equalf(t, expected.ConnectTimeout, actual.ConnectTimeout, "%s - ConnectTimeout", testName) 639 assert.Equalf(t, expected.RuntimeParams, actual.RuntimeParams, "%s - RuntimeParams", testName) 640 641 // Can't test function equality, so just test that they are set or not. 642 assert.Equalf(t, expected.ValidateConnect == nil, actual.ValidateConnect == nil, "%s - ValidateConnect", testName) 643 assert.Equalf(t, expected.AfterConnect == nil, actual.AfterConnect == nil, "%s - AfterConnect", testName) 644 645 if assert.Equalf(t, expected.TLSConfig == nil, actual.TLSConfig == nil, "%s - TLSConfig", testName) { 646 if expected.TLSConfig != nil { 647 assert.Equalf(t, expected.TLSConfig.InsecureSkipVerify, actual.TLSConfig.InsecureSkipVerify, "%s - TLSConfig InsecureSkipVerify", testName) 648 assert.Equalf(t, expected.TLSConfig.ServerName, actual.TLSConfig.ServerName, "%s - TLSConfig ServerName", testName) 649 } 650 } 651 652 if assert.Equalf(t, len(expected.Fallbacks), len(actual.Fallbacks), "%s - Fallbacks", testName) { 653 for i := range expected.Fallbacks { 654 assert.Equalf(t, expected.Fallbacks[i].Host, actual.Fallbacks[i].Host, "%s - Fallback %d - Host", testName, i) 655 assert.Equalf(t, expected.Fallbacks[i].Port, actual.Fallbacks[i].Port, "%s - Fallback %d - Port", testName, i) 656 657 if assert.Equalf(t, expected.Fallbacks[i].TLSConfig == nil, actual.Fallbacks[i].TLSConfig == nil, "%s - Fallback %d - TLSConfig", testName, i) { 658 if expected.Fallbacks[i].TLSConfig != nil { 659 assert.Equalf(t, expected.Fallbacks[i].TLSConfig.InsecureSkipVerify, actual.Fallbacks[i].TLSConfig.InsecureSkipVerify, "%s - Fallback %d - TLSConfig InsecureSkipVerify", testName) 660 assert.Equalf(t, expected.Fallbacks[i].TLSConfig.ServerName, actual.Fallbacks[i].TLSConfig.ServerName, "%s - Fallback %d - TLSConfig ServerName", testName) 661 } 662 } 663 } 664 } 665} 666 667func TestParseConfigEnvLibpq(t *testing.T) { 668 var osUserName string 669 osUser, err := user.Current() 670 if err == nil { 671 // Windows gives us the username here as `DOMAIN\user` or `LOCALPCNAME\user`, 672 // but the libpq default is just the `user` portion, so we strip off the first part. 673 if runtime.GOOS == "windows" && strings.Contains(osUser.Username, "\\") { 674 osUserName = osUser.Username[strings.LastIndex(osUser.Username, "\\")+1:] 675 } else { 676 osUserName = osUser.Username 677 } 678 } 679 680 pgEnvvars := []string{"PGHOST", "PGPORT", "PGDATABASE", "PGUSER", "PGPASSWORD", "PGAPPNAME", "PGSSLMODE", "PGCONNECT_TIMEOUT"} 681 682 savedEnv := make(map[string]string) 683 for _, n := range pgEnvvars { 684 savedEnv[n] = os.Getenv(n) 685 } 686 defer func() { 687 for k, v := range savedEnv { 688 err := os.Setenv(k, v) 689 if err != nil { 690 t.Fatalf("Unable to restore environment: %v", err) 691 } 692 } 693 }() 694 695 tests := []struct { 696 name string 697 envvars map[string]string 698 config *pgconn.Config 699 }{ 700 { 701 // not testing no environment at all as that would use default host and that can vary. 702 name: "PGHOST only", 703 envvars: map[string]string{"PGHOST": "123.123.123.123"}, 704 config: &pgconn.Config{ 705 User: osUserName, 706 Host: "123.123.123.123", 707 Port: 5432, 708 TLSConfig: &tls.Config{ 709 InsecureSkipVerify: true, 710 }, 711 RuntimeParams: map[string]string{}, 712 Fallbacks: []*pgconn.FallbackConfig{ 713 &pgconn.FallbackConfig{ 714 Host: "123.123.123.123", 715 Port: 5432, 716 TLSConfig: nil, 717 }, 718 }, 719 }, 720 }, 721 { 722 name: "All non-TLS environment", 723 envvars: map[string]string{ 724 "PGHOST": "123.123.123.123", 725 "PGPORT": "7777", 726 "PGDATABASE": "foo", 727 "PGUSER": "bar", 728 "PGPASSWORD": "baz", 729 "PGCONNECT_TIMEOUT": "10", 730 "PGSSLMODE": "disable", 731 "PGAPPNAME": "pgxtest", 732 }, 733 config: &pgconn.Config{ 734 Host: "123.123.123.123", 735 Port: 7777, 736 Database: "foo", 737 User: "bar", 738 Password: "baz", 739 ConnectTimeout: 10 * time.Second, 740 TLSConfig: nil, 741 RuntimeParams: map[string]string{"application_name": "pgxtest"}, 742 }, 743 }, 744 } 745 746 for i, tt := range tests { 747 for _, n := range pgEnvvars { 748 err := os.Unsetenv(n) 749 require.NoError(t, err) 750 } 751 752 for k, v := range tt.envvars { 753 err := os.Setenv(k, v) 754 require.NoError(t, err) 755 } 756 757 config, err := pgconn.ParseConfig("") 758 if !assert.Nilf(t, err, "Test %d (%s)", i, tt.name) { 759 continue 760 } 761 762 assertConfigsEqual(t, tt.config, config, fmt.Sprintf("Test %d (%s)", i, tt.name)) 763 } 764} 765 766func TestParseConfigReadsPgPassfile(t *testing.T) { 767 t.Parallel() 768 769 tf, err := ioutil.TempFile("", "") 770 require.NoError(t, err) 771 772 defer tf.Close() 773 defer os.Remove(tf.Name()) 774 775 _, err = tf.Write([]byte("test1:5432:curlydb:curly:nyuknyuknyuk")) 776 require.NoError(t, err) 777 778 connString := fmt.Sprintf("postgres://curly@test1:5432/curlydb?sslmode=disable&passfile=%s", tf.Name()) 779 expected := &pgconn.Config{ 780 User: "curly", 781 Password: "nyuknyuknyuk", 782 Host: "test1", 783 Port: 5432, 784 Database: "curlydb", 785 TLSConfig: nil, 786 RuntimeParams: map[string]string{}, 787 } 788 789 actual, err := pgconn.ParseConfig(connString) 790 assert.NoError(t, err) 791 792 assertConfigsEqual(t, expected, actual, "passfile") 793} 794 795func TestParseConfigReadsPgServiceFile(t *testing.T) { 796 t.Parallel() 797 798 tf, err := ioutil.TempFile("", "") 799 require.NoError(t, err) 800 801 defer tf.Close() 802 defer os.Remove(tf.Name()) 803 804 _, err = tf.Write([]byte(` 805[abc] 806host=abc.example.com 807port=9999 808dbname=abcdb 809user=abcuser 810 811[def] 812host = def.example.com 813dbname = defdb 814user = defuser 815application_name = spaced string 816`)) 817 require.NoError(t, err) 818 819 tests := []struct { 820 name string 821 connString string 822 config *pgconn.Config 823 }{ 824 { 825 name: "abc", 826 connString: fmt.Sprintf("postgres:///?servicefile=%s&service=%s", tf.Name(), "abc"), 827 config: &pgconn.Config{ 828 Host: "abc.example.com", 829 Database: "abcdb", 830 User: "abcuser", 831 Port: 9999, 832 TLSConfig: &tls.Config{ 833 InsecureSkipVerify: true, 834 }, 835 RuntimeParams: map[string]string{}, 836 Fallbacks: []*pgconn.FallbackConfig{ 837 &pgconn.FallbackConfig{ 838 Host: "abc.example.com", 839 Port: 9999, 840 TLSConfig: nil, 841 }, 842 }, 843 }, 844 }, 845 { 846 name: "def", 847 connString: fmt.Sprintf("postgres:///?servicefile=%s&service=%s", tf.Name(), "def"), 848 config: &pgconn.Config{ 849 Host: "def.example.com", 850 Port: 5432, 851 Database: "defdb", 852 User: "defuser", 853 TLSConfig: &tls.Config{ 854 InsecureSkipVerify: true, 855 }, 856 RuntimeParams: map[string]string{"application_name": "spaced string"}, 857 Fallbacks: []*pgconn.FallbackConfig{ 858 &pgconn.FallbackConfig{ 859 Host: "def.example.com", 860 Port: 5432, 861 TLSConfig: nil, 862 }, 863 }, 864 }, 865 }, 866 { 867 name: "conn string has precedence", 868 connString: fmt.Sprintf("postgres://other.example.com:7777/?servicefile=%s&service=%s&sslmode=disable", tf.Name(), "abc"), 869 config: &pgconn.Config{ 870 Host: "other.example.com", 871 Database: "abcdb", 872 User: "abcuser", 873 Port: 7777, 874 TLSConfig: nil, 875 RuntimeParams: map[string]string{}, 876 }, 877 }, 878 } 879 880 for i, tt := range tests { 881 config, err := pgconn.ParseConfig(tt.connString) 882 if !assert.NoErrorf(t, err, "Test %d (%s)", i, tt.name) { 883 continue 884 } 885 886 assertConfigsEqual(t, tt.config, config, fmt.Sprintf("Test %d (%s)", i, tt.name)) 887 } 888} 889 890func TestParseConfigExtractsMinReadBufferSize(t *testing.T) { 891 t.Parallel() 892 893 config, err := pgconn.ParseConfig("min_read_buffer_size=0") 894 require.NoError(t, err) 895 _, present := config.RuntimeParams["min_read_buffer_size"] 896 require.False(t, present) 897 898 // The buffer size is internal so there isn't much that can be done to test it other than see that the runtime param 899 // was removed. 900} 901