1 package pq 2 3 import ( 4 "context" 5 "database/sql/driver" 6 "errors" 7 "fmt" 8 "os" 9 "strings" 10 ) 11 12 // Connector represents a fixed configuration for the pq driver with a given 13 // name. Connector satisfies the database/sql/driver Connector interface and 14 // can be used to create any number of DB Conn's via the database/sql OpenDB 15 // function. 16 // 17 // See https://golang.org/pkg/database/sql/driver/#Connector. 18 // See https://golang.org/pkg/database/sql/#OpenDB. 19 type Connector struct { 20 opts values 21 dialer Dialer 22 } 23 24 // Connect returns a connection to the database using the fixed configuration 25 // of this Connector. Context is not used. 26 func (c *Connector) Connect(ctx context.Context) (driver.Conn, error) { 27 return c.open(ctx) 28 } 29 30 // Dialer allows change the dialer used to open connections. 31 func (c *Connector) Dialer(dialer Dialer) { 32 c.dialer = dialer 33 } 34 35 // Driver returns the underlying driver of this Connector. 36 func (c *Connector) Driver() driver.Driver { 37 return &Driver{} 38 } 39 40 // NewConnector returns a connector for the pq driver in a fixed configuration 41 // with the given dsn. The returned connector can be used to create any number 42 // of equivalent Conn's. The returned connector is intended to be used with 43 // database/sql.OpenDB. 44 // 45 // See https://golang.org/pkg/database/sql/driver/#Connector. 46 // See https://golang.org/pkg/database/sql/#OpenDB. 47 func NewConnector(dsn string) (*Connector, error) { 48 var err error 49 o := make(values) 50 51 // A number of defaults are applied here, in this order: 52 // 53 // * Very low precedence defaults applied in every situation 54 // * Environment variables 55 // * Explicitly passed connection information 56 o["host"] = "localhost" 57 o["port"] = "5432" 58 // N.B.: Extra float digits should be set to 3, but that breaks 59 // Postgres 8.4 and older, where the max is 2. 60 o["extra_float_digits"] = "2" 61 for k, v := range parseEnviron(os.Environ()) { 62 o[k] = v 63 } 64 65 if strings.HasPrefix(dsn, "postgres://") || strings.HasPrefix(dsn, "postgresql://") { 66 dsn, err = ParseURL(dsn) 67 if err != nil { 68 return nil, err 69 } 70 } 71 72 if err := parseOpts(dsn, o); err != nil { 73 return nil, err 74 } 75 76 // Use the "fallback" application name if necessary 77 if fallback, ok := o["fallback_application_name"]; ok { 78 if _, ok := o["application_name"]; !ok { 79 o["application_name"] = fallback 80 } 81 } 82 83 // We can't work with any client_encoding other than UTF-8 currently. 84 // However, we have historically allowed the user to set it to UTF-8 85 // explicitly, and there's no reason to break such programs, so allow that. 86 // Note that the "options" setting could also set client_encoding, but 87 // parsing its value is not worth it. Instead, we always explicitly send 88 // client_encoding as a separate run-time parameter, which should override 89 // anything set in options. 90 if enc, ok := o["client_encoding"]; ok && !isUTF8(enc) { 91 return nil, errors.New("client_encoding must be absent or 'UTF8'") 92 } 93 o["client_encoding"] = "UTF8" 94 // DateStyle needs a similar treatment. 95 if datestyle, ok := o["datestyle"]; ok { 96 if datestyle != "ISO, MDY" { 97 return nil, fmt.Errorf("setting datestyle must be absent or %v; got %v", "ISO, MDY", datestyle) 98 } 99 } else { 100 o["datestyle"] = "ISO, MDY" 101 } 102 103 // If a user is not provided by any other means, the last 104 // resort is to use the current operating system provided user 105 // name. 106 if _, ok := o["user"]; !ok { 107 u, err := userCurrent() 108 if err != nil { 109 return nil, err 110 } 111 o["user"] = u 112 } 113 114 // SSL is not necessary or supported over UNIX domain sockets 115 if network, _ := network(o); network == "unix" { 116 o["sslmode"] = "disable" 117 } 118 119 return &Connector{opts: o, dialer: defaultDialer{}}, nil 120 } 121