Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions db/conn_fn.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package db

import (
"database/sql"
"sync"
)

var (
dbPool = map[string]*sql.DB{}
cmux sync.Mutex
)

// ConnFn is the database connection builder function that
// will be used by the application based on the driver and
// connection string.
type ConnFn func() (*sql.DB, error)

// ConnectionFn is the database connection builder function that
// will be used by the application based on the driver and
// connection string. It opens the connection only once
// and return the same connection on subsequent calls.
func ConnectionFn(url string, opts ...connectionOption) ConnFn {
return func() (cx *sql.DB, err error) {
cmux.Lock()
defer cmux.Unlock()

// Return existing connection if available and valid
// to avoid reopening connections. Otherwise continue
// to create a new one.
if conn := dbPool[url]; conn != nil && conn.Ping() == nil {
return conn, nil
}

cs := &connSettings{
url: url,

driver: "postgres",
params: "",
}

// Apply options before connecting to the database.
for _, v := range opts {
v(cs)
}

conn, err := sql.Open(cs.driver, cs.connectionURL())
if err != nil {
return nil, err
}

// This uses url instead of modURL while the params apply
// to all connections.
dbPool[url] = conn

return conn, nil
}
}
35 changes: 35 additions & 0 deletions db/conn_options.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package db

import "net/url"

// connectionOptions for the database
type connectionOption func(*connSettings)

// WithDriver allows to specify the driver to use driver defaults to
// postgres.
func WithDriver(name string) connectionOption {
return func(cs *connSettings) {
cs.driver = name
}
}

// Params allows to specify additional connection parameters
// that will be encoded as URL params next to the connection string.
// params should be in key,value,key,value,... format.
// e.g Params("sslmode", "disable", "timezone", "UTC")
func Params(params ...string) connectionOption {
vals := url.Values{}
for i := 0; i < len(params); i += 2 {
key := params[i]
if i+1 >= len(params) {
vals.Add(key, "")
break
}

vals.Add(key, params[i+1])
}

return func(cs *connSettings) {
cs.params = vals.Encode()
}
}
24 changes: 24 additions & 0 deletions db/conn_settings.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package db

import "strings"

// connSettings holds database connection settings.
// while opening the connection, its modified by
// connectionOption functions.
type connSettings struct {
params string
driver string
url string
}

func (cs connSettings) connectionURL() string {
if cs.params == "" {
return cs.url
}

if strings.Contains(cs.url, "?") {
return cs.url + "&" + cs.params
}

return cs.url + "?" + cs.params
}
File renamed without changes.
96 changes: 0 additions & 96 deletions db/connection.go

This file was deleted.

20 changes: 11 additions & 9 deletions db/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,13 @@ import (
"strings"
)

var (
// postgresURLExp is the regular expression to extract the database name
// and the user credentials from the database URL.
postgresURLExp = regexp.MustCompile(`postgres://(.*):(.*)@(.*):(.*)/([^?]*)`)
)
// postgresURLExp is the regular expression to extract the database name
// and the user credentials from the database URL.
var postgresURLExp = regexp.MustCompile(`postgres://(.*):(.*)@(.*):(.*)/([^?]*)`)

// Create a new database based on the passed URL.
func Create(url string) error {
var createFn func(string) error = createSQLite
createFn := createSQLite
if strings.Contains(url, "postgres") {
createFn = createPostgres
}
Expand Down Expand Up @@ -48,15 +46,19 @@ func createPostgres(conURL string) error {
return fmt.Errorf("invalid database URL: %s", conURL)
}

db, err := sql.Open("postgres", fmt.Sprintf("postgres://%s:%s@%s:%s/postgres?sslmode=disable", matches[1], matches[2], matches[3], matches[4]))
dsn := fmt.Sprintf(
"postgres://%s:%s@%s:%s/postgres?sslmode=disable",
matches[1], matches[2], matches[3], matches[4],
)

db, err := sql.Open("postgres", dsn)
if err != nil {
return fmt.Errorf("connecting to database: %w", err)
}

var exists int
row := db.QueryRow("SELECT COUNT(datname) FROM pg_database WHERE datname ilike $1", matches[5])
err = row.Scan(&exists)

if err != nil {
return err
}
Expand All @@ -75,7 +77,7 @@ func createPostgres(conURL string) error {

// Drop a database based on the passed URL.
func Drop(url string) error {
var dropFn func(string) error = dropSQLite
dropFn := dropSQLite
if strings.Contains(url, "postgres") {
dropFn = dropPostgres
}
Expand Down
Loading