terraform/builtin/providers/postgresql/config.go

98 lines
2.4 KiB
Go
Raw Normal View History

package postgresql
import (
2016-12-25 14:53:12 +01:00
"bytes"
"database/sql"
"fmt"
"log"
"sync"
"unicode"
"github.com/hashicorp/errwrap"
_ "github.com/lib/pq" //PostgreSQL db
)
// Config - provider config
type Config struct {
Host string
Port int
Database string
Username string
Password string
SSLMode string
ApplicationName string
Timeout int
ConnectTimeoutSec int
}
// Client struct holding connection string
type Client struct {
username string
connStr string
// PostgreSQL lock on pg_catalog. Many of the operations that Terraform
// performs are not permitted to be concurrent. Unlike traditional
// PostgreSQL tables that use MVCC, many of the PostgreSQL system
// catalogs look like tables, but are not in-fact able to be
// concurrently updated.
catalogLock sync.RWMutex
}
// NewClient returns new client config
func (c *Config) NewClient() (*Client, error) {
// NOTE: dbname must come before user otherwise dbname will be set to
// user.
2016-12-25 14:53:12 +01:00
const dsnFmt = "host=%s port=%d dbname=%s user=%s password=%s sslmode=%s fallback_application_name=%s connect_timeout=%d"
2016-12-25 14:53:12 +01:00
// Quote empty strings or strings that contain whitespace
q := func(s string) string {
b := bytes.NewBufferString(`'`)
b.Grow(len(s) + 2)
var haveWhitespace bool
2016-12-25 14:53:12 +01:00
for _, r := range s {
if unicode.IsSpace(r) {
haveWhitespace = true
}
2016-12-25 14:53:12 +01:00
switch r {
case '\'':
b.WriteString(`\'`)
case '\\':
b.WriteString(`\\`)
default:
b.WriteRune(r)
}
}
b.WriteString(`'`)
str := b.String()
if haveWhitespace || len(str) == 2 {
return str
}
return str[1 : len(str)-1]
2016-12-25 14:53:12 +01:00
}
logDSN := fmt.Sprintf(dsnFmt, q(c.Host), c.Port, q(c.Database), q(c.Username), q("<redacted>"), q(c.SSLMode), q(c.ApplicationName), c.ConnectTimeoutSec)
log.Printf("[INFO] PostgreSQL DSN: `%s`", logDSN)
2016-12-25 14:53:12 +01:00
connStr := fmt.Sprintf(dsnFmt, q(c.Host), c.Port, q(c.Database), q(c.Username), q(c.Password), q(c.SSLMode), q(c.ApplicationName), c.ConnectTimeoutSec)
client := Client{
connStr: connStr,
username: c.Username,
}
return &client, nil
}
// Connect will manually connect/disconnect to prevent a large
// number or db connections being made
func (c *Client) Connect() (*sql.DB, error) {
db, err := sql.Open("postgres", c.connStr)
if err != nil {
return nil, errwrap.Wrapf("Error connecting to PostgreSQL server: {{err}}", err)
}
return db, nil
}