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 | }
|
---|