597 lines
12 KiB
Go
597 lines
12 KiB
Go
|
package dburl
|
||
|
|
||
|
import (
|
||
|
"net/url"
|
||
|
stdpath "path"
|
||
|
"strings"
|
||
|
)
|
||
|
|
||
|
// GenScheme returns a func that generates a scheme:// style DSN from the
|
||
|
// passed URL.
|
||
|
func GenScheme(scheme string) func(*URL) (string, error) {
|
||
|
return func(u *URL) (string, error) {
|
||
|
z := &url.URL{
|
||
|
Scheme: scheme,
|
||
|
Opaque: u.Opaque,
|
||
|
User: u.User,
|
||
|
Host: u.Host,
|
||
|
Path: u.Path,
|
||
|
RawPath: u.RawPath,
|
||
|
RawQuery: u.RawQuery,
|
||
|
Fragment: u.Fragment,
|
||
|
}
|
||
|
|
||
|
return z.String(), nil
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// GenFromURL returns a func that generates a DSN using urlstr as the default
|
||
|
// URL parameters, overriding the values only if when in the passed URL.
|
||
|
func GenFromURL(urlstr string) func(*URL) (string, error) {
|
||
|
z, err := url.Parse(urlstr)
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
|
||
|
return func(u *URL) (string, error) {
|
||
|
opaque := z.Opaque
|
||
|
if u.Opaque != "" {
|
||
|
opaque = u.Opaque
|
||
|
}
|
||
|
|
||
|
user := z.User
|
||
|
if u.User != nil {
|
||
|
user = u.User
|
||
|
}
|
||
|
|
||
|
host, port := hostname(z.Host), hostport(z.Host)
|
||
|
if h := hostname(u.Host); h != "" {
|
||
|
host = h
|
||
|
}
|
||
|
if p := hostport(u.Host); p != "" {
|
||
|
port = p
|
||
|
}
|
||
|
if port != "" {
|
||
|
host += ":" + port
|
||
|
}
|
||
|
|
||
|
path := z.Path
|
||
|
if u.Path != "" {
|
||
|
path = u.Path
|
||
|
}
|
||
|
|
||
|
rawPath := z.RawPath
|
||
|
if u.RawPath != "" {
|
||
|
rawPath = u.RawPath
|
||
|
}
|
||
|
|
||
|
q := z.Query()
|
||
|
for k, v := range u.Query() {
|
||
|
q.Set(k, strings.Join(v, " "))
|
||
|
}
|
||
|
|
||
|
fragment := z.Fragment
|
||
|
if u.Fragment != "" {
|
||
|
fragment = u.Fragment
|
||
|
}
|
||
|
|
||
|
y := &url.URL{
|
||
|
Scheme: z.Scheme,
|
||
|
Opaque: opaque,
|
||
|
User: user,
|
||
|
Host: host,
|
||
|
Path: path,
|
||
|
RawPath: rawPath,
|
||
|
RawQuery: q.Encode(),
|
||
|
Fragment: fragment,
|
||
|
}
|
||
|
|
||
|
return y.String(), nil
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// GenOpaque generates a opaque file path DSN from the passed URL.
|
||
|
func GenOpaque(u *URL) (string, error) {
|
||
|
if u.Opaque == "" {
|
||
|
return "", ErrMissingPath
|
||
|
}
|
||
|
|
||
|
return u.Opaque + genQueryOptions(u.Query()), nil
|
||
|
}
|
||
|
|
||
|
// GenPostgres generates a postgres DSN from the passed URL.
|
||
|
func GenPostgres(u *URL) (string, error) {
|
||
|
host, port, dbname := hostname(u.Host), hostport(u.Host), strings.TrimPrefix(u.Path, "/")
|
||
|
if host == "." {
|
||
|
return "", ErrRelativePathNotSupported
|
||
|
}
|
||
|
|
||
|
// resolve path
|
||
|
if u.Proto == "unix" {
|
||
|
if host == "" {
|
||
|
dbname = "/" + dbname
|
||
|
}
|
||
|
|
||
|
host, port, dbname = resolveDir(stdpath.Join(host, dbname))
|
||
|
}
|
||
|
|
||
|
q := u.Query()
|
||
|
q.Set("host", host)
|
||
|
q.Set("port", port)
|
||
|
q.Set("dbname", dbname)
|
||
|
|
||
|
// add user/pass
|
||
|
if u.User != nil {
|
||
|
q.Set("user", u.User.Username())
|
||
|
pass, _ := u.User.Password()
|
||
|
q.Set("password", pass)
|
||
|
}
|
||
|
|
||
|
// save host, port, dbname
|
||
|
if u.hostPortDB == nil {
|
||
|
u.hostPortDB = []string{host, port, dbname}
|
||
|
}
|
||
|
|
||
|
return genOptions(q, "", "=", " ", ",", true), nil
|
||
|
}
|
||
|
|
||
|
// GenSQLServer generates a mssql DSN from the passed URL.
|
||
|
func GenSQLServer(u *URL) (string, error) {
|
||
|
host, port, dbname := hostname(u.Host), hostport(u.Host), strings.TrimPrefix(u.Path, "/")
|
||
|
|
||
|
// add instance name to host if present
|
||
|
if i := strings.Index(dbname, "/"); i != -1 {
|
||
|
host = host + `\` + dbname[:i]
|
||
|
dbname = dbname[i+1:]
|
||
|
}
|
||
|
|
||
|
q := u.Query()
|
||
|
q.Set("Server", host)
|
||
|
q.Set("Port", port)
|
||
|
q.Set("Database", dbname)
|
||
|
|
||
|
// add user/pass
|
||
|
if u.User != nil {
|
||
|
q.Set("User ID", u.User.Username())
|
||
|
pass, _ := u.User.Password()
|
||
|
q.Set("Password", pass)
|
||
|
}
|
||
|
|
||
|
// save host, port, dbname
|
||
|
if u.hostPortDB == nil {
|
||
|
u.hostPortDB = []string{host, port, dbname}
|
||
|
}
|
||
|
|
||
|
return genOptionsODBC(q, true), nil
|
||
|
}
|
||
|
|
||
|
// // GenSybase generates a sqlany DSN from the passed URL.
|
||
|
// func GenSybase(u *URL) (string, error) {
|
||
|
// // of format "UID=DBA;PWD=sql;Host=demo12;DatabaseName=demo;ServerName=myserver"
|
||
|
// host, port, dbname := hostname(u.Host), hostport(u.Host), strings.TrimPrefix(u.Path, "/")
|
||
|
//
|
||
|
// // add instance name to host if present
|
||
|
// if i := strings.Index(dbname, "/"); i != -1 {
|
||
|
// host = host + `\` + dbname[:i]
|
||
|
// dbname = dbname[i+1:]
|
||
|
// }
|
||
|
//
|
||
|
// q := u.Query()
|
||
|
// q.Set("Host", host)
|
||
|
// if port != "" {
|
||
|
// q.Set("LINKS", "tcpip(PORT="+port+")")
|
||
|
// }
|
||
|
// q.Set("DatabaseName", dbname)
|
||
|
//
|
||
|
// // add user/pass
|
||
|
// if u.User != nil {
|
||
|
// q.Set("UID", u.User.Username())
|
||
|
// pass, _ := u.User.Password()
|
||
|
// q.Set("PWD", pass)
|
||
|
// }
|
||
|
//
|
||
|
// // save host, port, dbname
|
||
|
// if u.hostPortDB == nil {
|
||
|
// u.hostPortDB = []string{host, port, dbname}
|
||
|
// }
|
||
|
//
|
||
|
// return genOptionsODBC(q, true), nil
|
||
|
// }
|
||
|
|
||
|
// GenMySQL generates a mysql DSN from the passed URL.
|
||
|
func GenMySQL(u *URL) (string, error) {
|
||
|
host, port, dbname := hostname(u.Host), hostport(u.Host), strings.TrimPrefix(u.Path, "/")
|
||
|
|
||
|
var dsn string
|
||
|
|
||
|
// build user/pass
|
||
|
if u.User != nil {
|
||
|
if un := u.User.Username(); len(un) > 0 {
|
||
|
if up, ok := u.User.Password(); ok {
|
||
|
un += ":" + up
|
||
|
}
|
||
|
dsn += un + "@"
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// resolve path
|
||
|
if u.Proto == "unix" {
|
||
|
if host == "" {
|
||
|
dbname = "/" + dbname
|
||
|
}
|
||
|
host, dbname = resolveSocket(stdpath.Join(host, dbname))
|
||
|
port = ""
|
||
|
}
|
||
|
|
||
|
// save host, port, dbname
|
||
|
if u.hostPortDB == nil {
|
||
|
u.hostPortDB = []string{host, port, dbname}
|
||
|
}
|
||
|
|
||
|
// if host or proto is not empty
|
||
|
if u.Proto != "unix" {
|
||
|
if host == "" {
|
||
|
host = "127.0.0.1"
|
||
|
}
|
||
|
if port == "" {
|
||
|
port = "3306"
|
||
|
}
|
||
|
}
|
||
|
if port != "" {
|
||
|
port = ":" + port
|
||
|
}
|
||
|
|
||
|
dsn += u.Proto + "(" + host + port + ")"
|
||
|
|
||
|
// add database name
|
||
|
dsn += "/" + dbname
|
||
|
|
||
|
return dsn + genQueryOptions(u.Query()), nil
|
||
|
}
|
||
|
|
||
|
// GenMyMySQL generates a MyMySQL MySQL DSN from the passed URL.
|
||
|
func GenMyMySQL(u *URL) (string, error) {
|
||
|
host, port, dbname := hostname(u.Host), hostport(u.Host), strings.TrimPrefix(u.Path, "/")
|
||
|
|
||
|
// resolve path
|
||
|
if u.Proto == "unix" {
|
||
|
if host == "" {
|
||
|
dbname = "/" + dbname
|
||
|
}
|
||
|
host, dbname = resolveSocket(stdpath.Join(host, dbname))
|
||
|
port = ""
|
||
|
}
|
||
|
|
||
|
// save host, port, dbname
|
||
|
if u.hostPortDB == nil {
|
||
|
u.hostPortDB = []string{host, port, dbname}
|
||
|
}
|
||
|
|
||
|
// if host or proto is not empty
|
||
|
if u.Proto != "unix" {
|
||
|
if host == "" {
|
||
|
host = "127.0.0.1"
|
||
|
}
|
||
|
if port == "" {
|
||
|
port = "3306"
|
||
|
}
|
||
|
}
|
||
|
if port != "" {
|
||
|
port = ":" + port
|
||
|
}
|
||
|
|
||
|
dsn := u.Proto + ":" + host + port
|
||
|
|
||
|
// add opts
|
||
|
dsn += genOptions(
|
||
|
convertOptions(u.Query(), "true", ""),
|
||
|
",", "=", ",", " ", false,
|
||
|
)
|
||
|
|
||
|
// add dbname
|
||
|
dsn += "*" + dbname
|
||
|
|
||
|
// add user/pass
|
||
|
if u.User != nil {
|
||
|
pass, _ := u.User.Password()
|
||
|
dsn += "/" + u.User.Username() + "/" + pass
|
||
|
} else if strings.HasSuffix(dsn, "*") {
|
||
|
dsn += "//"
|
||
|
}
|
||
|
|
||
|
return dsn, nil
|
||
|
}
|
||
|
|
||
|
// GenOracle generates a ora DSN from the passed URL.
|
||
|
func GenOracle(u *URL) (string, error) {
|
||
|
// create dsn
|
||
|
dsn := u.Host + u.Path
|
||
|
|
||
|
// build user/pass
|
||
|
var un string
|
||
|
if u.User != nil {
|
||
|
if un = u.User.Username(); len(un) > 0 {
|
||
|
if up, ok := u.User.Password(); ok {
|
||
|
un += "/" + up
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return un + "@" + dsn, nil
|
||
|
}
|
||
|
|
||
|
// GenFirebird generates a firebirdsql DSN from the passed URL.
|
||
|
func GenFirebird(u *URL) (string, error) {
|
||
|
z := &url.URL{
|
||
|
User: u.User,
|
||
|
Host: u.Host,
|
||
|
Path: u.Path,
|
||
|
RawPath: u.RawPath,
|
||
|
RawQuery: u.RawQuery,
|
||
|
Fragment: u.Fragment,
|
||
|
}
|
||
|
return strings.TrimPrefix(z.String(), "//"), nil
|
||
|
}
|
||
|
|
||
|
// GenADODB generates a adodb DSN from the passed URL.
|
||
|
func GenADODB(u *URL) (string, error) {
|
||
|
// grab data source
|
||
|
host, port := hostname(u.Host), hostport(u.Host)
|
||
|
dsname, dbname := strings.TrimPrefix(u.Path, "/"), ""
|
||
|
if dsname == "" {
|
||
|
dsname = "."
|
||
|
}
|
||
|
|
||
|
// check if data source is not a path on disk
|
||
|
if mode(dsname) == 0 {
|
||
|
if i := strings.IndexAny(dsname, `\/`); i != -1 {
|
||
|
dbname = dsname[i+1:]
|
||
|
dsname = dsname[:i]
|
||
|
}
|
||
|
}
|
||
|
|
||
|
q := u.Query()
|
||
|
q.Set("Provider", host)
|
||
|
q.Set("Port", port)
|
||
|
q.Set("Data Source", dsname)
|
||
|
q.Set("Database", dbname)
|
||
|
|
||
|
// add user/pass
|
||
|
if u.User != nil {
|
||
|
q.Set("User ID", u.User.Username())
|
||
|
pass, _ := u.User.Password()
|
||
|
q.Set("Password", pass)
|
||
|
}
|
||
|
|
||
|
// save host, port, dbname
|
||
|
if u.hostPortDB == nil {
|
||
|
n := dsname
|
||
|
if dbname != "" {
|
||
|
n += "/" + dbname
|
||
|
}
|
||
|
u.hostPortDB = []string{host, port, n}
|
||
|
}
|
||
|
|
||
|
return genOptionsODBC(q, true), nil
|
||
|
}
|
||
|
|
||
|
// GenODBC generates a odbc DSN from the passed URL.
|
||
|
func GenODBC(u *URL) (string, error) {
|
||
|
q := u.Query()
|
||
|
|
||
|
host, port, dbname := hostname(u.Host), hostport(u.Host), strings.TrimPrefix(u.Path, "/")
|
||
|
|
||
|
// save host, port, dbname
|
||
|
if u.hostPortDB == nil {
|
||
|
u.hostPortDB = []string{host, port, dbname}
|
||
|
}
|
||
|
|
||
|
q.Set("Driver", "{"+strings.Replace(u.Proto, "+", " ", -1)+"}")
|
||
|
q.Set("Server", host)
|
||
|
|
||
|
if port == "" {
|
||
|
proto := strings.ToLower(u.Proto)
|
||
|
switch {
|
||
|
case strings.Contains(proto, "mysql"):
|
||
|
q.Set("Port", "3306")
|
||
|
case strings.Contains(proto, "postgres"):
|
||
|
q.Set("Port", "5432")
|
||
|
case strings.Contains(proto, "db2") || strings.Contains(proto, "ibm"):
|
||
|
q.Set("ServiceName", "50000")
|
||
|
default:
|
||
|
q.Set("Port", "1433")
|
||
|
}
|
||
|
} else {
|
||
|
q.Set("Port", port)
|
||
|
}
|
||
|
q.Set("Database", dbname)
|
||
|
|
||
|
// add user/pass
|
||
|
if u.User != nil {
|
||
|
q.Set("UID", u.User.Username())
|
||
|
p, _ := u.User.Password()
|
||
|
q.Set("PWD", p)
|
||
|
}
|
||
|
|
||
|
return genOptionsODBC(q, true), nil
|
||
|
}
|
||
|
|
||
|
// GenOLEODBC generates a oleodbc DSN from the passed URL.
|
||
|
func GenOLEODBC(u *URL) (string, error) {
|
||
|
props, err := GenODBC(u)
|
||
|
if err != nil {
|
||
|
return "", nil
|
||
|
}
|
||
|
|
||
|
return `Provider=MSDASQL.1;Extended Properties="` + props + `"`, nil
|
||
|
}
|
||
|
|
||
|
// GenClickhouse generates a clickhouse DSN from the passed URL.
|
||
|
func GenClickhouse(u *URL) (string, error) {
|
||
|
z := &url.URL{
|
||
|
Scheme: "tcp",
|
||
|
Opaque: u.Opaque,
|
||
|
Host: u.Host,
|
||
|
Path: u.Path,
|
||
|
RawPath: u.RawPath,
|
||
|
RawQuery: u.RawQuery,
|
||
|
Fragment: u.Fragment,
|
||
|
}
|
||
|
|
||
|
if hostport(z.Host) == "" {
|
||
|
z.Host += ":9000"
|
||
|
}
|
||
|
|
||
|
// add parameters
|
||
|
q := z.Query()
|
||
|
if u.User != nil {
|
||
|
if user := u.User.Username(); len(user) > 0 {
|
||
|
q.Set("username", user)
|
||
|
}
|
||
|
if pass, ok := u.User.Password(); ok {
|
||
|
q.Set("password", pass)
|
||
|
}
|
||
|
}
|
||
|
z.RawQuery = q.Encode()
|
||
|
|
||
|
return z.String(), nil
|
||
|
}
|
||
|
|
||
|
// GenVoltDB generates a VoltDB DSN from the passed URL.
|
||
|
func GenVoltDB(u *URL) (string, error) {
|
||
|
host, port := "localhost", "21212"
|
||
|
if h := hostname(u.Host); h != "" {
|
||
|
host = h
|
||
|
}
|
||
|
if p := hostport(u.Host); p != "" {
|
||
|
port = p
|
||
|
}
|
||
|
return host + ":" + port, nil
|
||
|
}
|
||
|
|
||
|
// GenPresto generates a Presto DSN from the passed URL.
|
||
|
func GenPresto(u *URL) (string, error) {
|
||
|
z := &url.URL{
|
||
|
Scheme: "http",
|
||
|
Opaque: u.Opaque,
|
||
|
User: u.User,
|
||
|
Host: u.Host,
|
||
|
RawQuery: u.RawQuery,
|
||
|
Fragment: u.Fragment,
|
||
|
}
|
||
|
|
||
|
// change to https
|
||
|
if strings.HasSuffix(u.OriginalScheme, "s") {
|
||
|
z.Scheme = "https"
|
||
|
}
|
||
|
|
||
|
// force user
|
||
|
if z.User == nil {
|
||
|
z.User = url.User("user")
|
||
|
}
|
||
|
|
||
|
// force host
|
||
|
if z.Host == "" {
|
||
|
z.Host = "localhost"
|
||
|
}
|
||
|
|
||
|
// force port
|
||
|
if hostport(z.Host) == "" {
|
||
|
if z.Scheme == "http" {
|
||
|
z.Host += ":8080"
|
||
|
} else if z.Scheme == "https" {
|
||
|
z.Host += ":8443"
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// add parameters
|
||
|
q := z.Query()
|
||
|
dbname, schema := strings.TrimPrefix(u.Path, "/"), ""
|
||
|
if dbname == "" {
|
||
|
dbname = "default"
|
||
|
} else if i := strings.Index(dbname, "/"); i != -1 {
|
||
|
schema, dbname = dbname[i+1:], dbname[:i]
|
||
|
}
|
||
|
q.Set("catalog", dbname)
|
||
|
if schema != "" {
|
||
|
q.Set("schema", schema)
|
||
|
}
|
||
|
z.RawQuery = q.Encode()
|
||
|
|
||
|
return z.String(), nil
|
||
|
}
|
||
|
|
||
|
// GenCassandra generates a cassandra DSN from the passed URL.
|
||
|
func GenCassandra(u *URL) (string, error) {
|
||
|
host, port, dbname := "localhost", "9042", strings.TrimPrefix(u.Path, "/")
|
||
|
if h := hostname(u.Host); h != "" {
|
||
|
host = h
|
||
|
}
|
||
|
if p := hostport(u.Host); p != "" {
|
||
|
port = p
|
||
|
}
|
||
|
q := u.Query()
|
||
|
// add user/pass
|
||
|
if u.User != nil {
|
||
|
q.Set("username", u.User.Username())
|
||
|
if pass, _ := u.User.Password(); pass != "" {
|
||
|
q.Set("password", pass)
|
||
|
}
|
||
|
}
|
||
|
// add dbname
|
||
|
if dbname != "" {
|
||
|
q.Set("keyspace", dbname)
|
||
|
}
|
||
|
return host + ":" + port + genQueryOptions(q), nil
|
||
|
}
|
||
|
|
||
|
// GenIgnite generates an ignite DSN from the passed URL.
|
||
|
func GenIgnite(u *URL) (string, error) {
|
||
|
host, port, dbname := "localhost", "10800", strings.TrimPrefix(u.Path, "/")
|
||
|
if h := hostname(u.Host); h != "" {
|
||
|
host = h
|
||
|
}
|
||
|
if p := hostport(u.Host); p != "" {
|
||
|
port = p
|
||
|
}
|
||
|
q := u.Query()
|
||
|
// add user/pass
|
||
|
if u.User != nil {
|
||
|
q.Set("username", u.User.Username())
|
||
|
if pass, _ := u.User.Password(); pass != "" {
|
||
|
q.Set("password", pass)
|
||
|
}
|
||
|
}
|
||
|
// add dbname
|
||
|
if dbname != "" {
|
||
|
dbname = "/" + dbname
|
||
|
}
|
||
|
return "tcp://" + host + ":" + port + dbname + genQueryOptions(q), nil
|
||
|
}
|
||
|
|
||
|
// GenSnowflake generates a snowflake DSN from the passed URL.
|
||
|
func GenSnowflake(u *URL) (string, error) {
|
||
|
host, port, dbname := hostname(u.Host), hostport(u.Host), strings.TrimPrefix(u.Path, "/")
|
||
|
if host == "" {
|
||
|
return "", ErrMissingHost
|
||
|
}
|
||
|
if dbname == "" {
|
||
|
return "", ErrMissingPath
|
||
|
}
|
||
|
if port != "" {
|
||
|
port = ":" + port
|
||
|
}
|
||
|
|
||
|
// add user/pass
|
||
|
var user string
|
||
|
if u.User != nil {
|
||
|
user = u.User.Username()
|
||
|
if pass, _ := u.User.Password(); pass != "" {
|
||
|
user += ":" + pass
|
||
|
}
|
||
|
user += "@"
|
||
|
}
|
||
|
|
||
|
return user + host + port + "/" + dbname + genQueryOptions(u.Query()), nil
|
||
|
}
|