...

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

Documentation: github.com/jackc/pgconn

     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  		// 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  					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  		// https://github.com/jackc/pgconn/issues/72
   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  // https://github.com/jackc/pgconn/issues/47
   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  // https://github.com/jackc/pgconn/issues/49
   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  	// Can't test function equality, so just test that they are set or not.
   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  		// Windows gives us the username here as `DOMAIN\user` or `LOCALPCNAME\user`,
   899  		// but the libpq default is just the `user` portion, so we strip off the first part.
   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  			// not testing no environment at all as that would use default host and that can vary.
   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  	// The buffer size is internal so there isn't much that can be done to test it other than see that the runtime param
  1145  	// was removed.
  1146  }
  1147  

View as plain text