...

Source file src/github.com/jackc/pgx/v5/pgconn/config_test.go

Documentation: github.com/jackc/pgx/v5/pgconn

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

View as plain text