-
Notifications
You must be signed in to change notification settings - Fork 15
/
dsn.go
119 lines (99 loc) · 2.22 KB
/
dsn.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
package db
import (
"fmt"
"net/url"
"os"
"sort"
"strconv"
"strings"
"github.com/drone/envsubst"
)
type DSN struct {
driver string
original string
host string
port int64
username string
password string
database string
schema string
options []string
}
var driverMap = map[string]string{
"psql": "postgres",
"postgres": "postgres",
"clickhouse": "clickhouse",
}
func ParseDSN(dsn string) (*DSN, error) {
expanded, err := envsubst.Eval(dsn, os.Getenv)
if err != nil {
return nil, fmt.Errorf("variables expansion failed: %w", err)
}
dsnURL, err := url.Parse(expanded)
if err != nil {
return nil, fmt.Errorf("invalid url: %w", err)
}
driver, ok := driverMap[dsnURL.Scheme]
if !ok {
keys := make([]string, len(driverMap))
i := 0
for k := range driverMap {
keys[i] = k
i++
}
return nil, fmt.Errorf("invalid scheme %s, allowed schemes: [%s]", dsnURL.Scheme, strings.Join(keys, ","))
}
host := dsnURL.Hostname()
port := int64(5432)
if strings.Contains(dsnURL.Host, ":") {
port, _ = strconv.ParseInt(dsnURL.Port(), 10, 32)
}
username := dsnURL.User.Username()
password, _ := dsnURL.User.Password()
database := strings.TrimPrefix(dsnURL.EscapedPath(), "/")
query := dsnURL.Query()
keys := make([]string, 0, len(query))
for key := range dsnURL.Query() {
keys = append(keys, key)
}
sort.Strings(keys)
d := &DSN{
original: dsn,
driver: driver,
host: host,
port: port,
username: username,
password: password,
database: database,
schema: "public",
}
if driver == "clickhouse" {
d.schema = database
}
options := make([]string, len(query))
for i, key := range keys {
if key == "schema" {
d.schema = query[key][0]
continue
}
options[i] = fmt.Sprintf("%s=%s", key, strings.Join(query[key], ","))
}
d.options = options
return d, nil
}
func (c *DSN) Driver() string {
return c.driver
}
func (c *DSN) ConnString() string {
if c.driver == "clickhouse" {
return c.original
}
out := fmt.Sprintf("host=%s port=%d user=%s dbname=%s %s", c.host, c.port, c.username, c.database, strings.Join(c.options, " "))
if c.password != "" {
out = out + " password=" + c.password
}
return out
}
func (c *DSN) Schema() string {
return c.schema
}