Add db-connect, a SQL over HTTPS server

This commit is contained in:
Ashcon Partovi
2019-11-12 10:50:41 -08:00
parent 13bf65ce4e
commit 759cd019be
396 changed files with 362808 additions and 649 deletions

2
vendor/github.com/xo/dburl/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,2 @@
example/example
coverage.out

9
vendor/github.com/xo/dburl/.travis.yml generated vendored Normal file
View File

@@ -0,0 +1,9 @@
language: go
go:
- 1.12.x
- tip
services:
- mysql
- postgresql
script:
- go test -v -coverprofile=coverage.out

21
vendor/github.com/xo/dburl/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2015-2019 Kenneth Shaw
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

248
vendor/github.com/xo/dburl/README.md generated vendored Normal file
View File

@@ -0,0 +1,248 @@
# dburl [![GoDoc][godoc]][godoc-link] [![Build Status][travis-ci]][travis-ci-link]
Package `dburl` provides a standard, URL style mechanism for parsing and
opening SQL database connection strings for [Go][go-project]. Provides
standardized way to [parse][godoc-parse] and [open][godoc-open] URLs for
popular databases PostgreSQL, MySQL, SQLite3, Oracle Database, Microsoft SQL
Server, in addition to most other SQL databases with a publicly available Go
driver.
[godoc]: https://godoc.org/github.com/xo/dburl?status.svg (GoDoc)
[travis-ci]: https://travis-ci.org/xo/dburl.svg?branch=master (Travis CI)
[godoc-link]: https://godoc.org/github.com/xo/dburl
[travis-ci-link]: https://travis-ci.org/xo/dburl
[Overview][] | [Quickstart][] | [Examples][] | [Schemes][] | [Installing][] | [Using][] | [About][]
[Overview]: #database-connection-url-overview (Database Connection URL Overview)
[Quickstart]: #quickstart (Quickstart)
[Examples]: #example-urls (Example URLs)
[Schemes]: #protocol-schemes-and-aliases (Protocol Schemes and Aliases)
[Installing]: #installing (Installing)
[Using]: #using (Using)
[About]: #about (About)
## Database Connection URL Overview
Supported database connection URLs are of the form:
```
protocol+transport://user:pass@host/dbname?opt1=a&opt2=b
protocol:/path/to/file
```
Where:
| Component | Description |
|--------------------|--------------------------------------------------------------------------------------|
| protocol | driver name or alias (see below) |
| transport | "tcp", "udp", "unix" or driver name (odbc/oleodbc) |
| user | username |
| pass | password |
| host | host |
| dbname<sup>*</sup> | database, instance, or service name/ID to connect to |
| ?opt1=... | additional database driver options (see respective SQL driver for available options) |
<i><sup><b>*</b></sup> for Microsoft SQL Server, `/dbname` can be
`/instance/dbname`, where `/instance` is optional. For Oracle Database,
`/dbname` is of the form `/service/dbname` where `/service` is the service name
or SID, and `/dbname` is optional. Please see below for examples.</i>
## Quickstart
Database connection URLs in the above format can be parsed with the
[`dburl.Parse` func][godoc-parse] as such:
```go
import (
"github.com/xo/dburl"
)
u, err := dburl.Parse("postgresql://user:pass@localhost/mydatabase/?sslmode=disable")
if err != nil { /* ... */ }
```
Additionally, a simple helper, [`dburl.Open`][godoc-open], is provided that
will parse, open, and return a [standard `sql.DB` database][godoc-sql-db]
connection:
```go
import (
"github.com/xo/dburl"
)
db, err := dburl.Open("sqlite:mydatabase.sqlite3?loc=auto")
if err != nil { /* ... */ }
```
## Example URLs
The following are example database connection URLs that can be handled by
[`dburl.Parse`][godoc-parse] and [`dburl.Open`][godoc-open]:
```
postgres://user:pass@localhost/dbname
pg://user:pass@localhost/dbname?sslmode=disable
mysql://user:pass@localhost/dbname
mysql:/var/run/mysqld/mysqld.sock
sqlserver://user:pass@remote-host.com/dbname
mssql://user:pass@remote-host.com/instance/dbname
ms://user:pass@remote-host.com:port/instance/dbname?keepAlive=10
oracle://user:pass@somehost.com/sid
sap://user:pass@localhost/dbname
sqlite:/path/to/file.db
file:myfile.sqlite3?loc=auto
odbc+postgres://user:pass@localhost:port/dbname?option1=
```
## Protocol Schemes and Aliases
The following protocols schemes (ie, driver) and their associated aliases are
supported out of the box:
| Database (scheme/driver) | Protocol Aliases [real driver] |
|------------------------------|-------------------------------------------|
| Microsoft SQL Server (mssql) | ms, sqlserver |
| MySQL (mysql) | my, mariadb, maria, percona, aurora |
| Oracle Database (goracle) | or, ora, oracle, oci, oci8, odpi, odpi-c |
| PostgreSQL (postgres) | pg, postgresql, pgsql |
| SQLite3 (sqlite3) | sq, sqlite, file |
| | |
| Amazon Redshift (redshift) | rs [postgres] |
| CockroachDB (cockroachdb) | cr, cockroach, crdb, cdb [postgres] |
| MemSQL (memsql) | me [mysql] |
| TiDB (tidb) | ti [mysql] |
| Vitess (vitess) | vt [mysql] |
| | |
| Google Spanner (spanner) | gs, google, span (not yet public) |
| | |
| MySQL (mymysql) | zm, mymy |
| PostgreSQL (pgx) | px |
| | |
| Apache Avatica (avatica) | av, phoenix |
| Apache Ignite (ignite) | ig, gridgain |
| Cassandra (cql) | ca, cassandra, datastax, scy, scylla |
| ClickHouse (clickhouse) | ch |
| Couchbase (n1ql) | n1, couchbase |
| Cznic QL (ql) | ql, cznic, cznicql |
| Firebird SQL (firebirdsql) | fb, firebird |
| Microsoft ADODB (adodb) | ad, ado |
| ODBC (odbc) | od |
| OLE ODBC (oleodbc) | oo, ole, oleodbc [adodb] |
| Presto (presto) | pr, prestodb, prestos, prs, prestodbs |
| SAP ASE (tds) | ax, ase, sapase |
| SAP HANA (hdb) | sa, saphana, sap, hana |
| Snowflake (snowflake) | sf |
| VoltDB (voltdb) | vo, volt, vdb |
Any protocol scheme `alias://` can be used in place of `protocol://`, and will
work identically with [`dburl.Parse`][godoc-parse] and [`dburl.Open`][godoc-open].
## Installing
Install in the usual Go fashion:
```sh
go get -u github.com/xo/dburl
```
## Using
Please note that `dburl` does not import actual SQL drivers, and only provides
a standard way to [parse][godoc-parse]/[open][godoc-open] respective database
connection URLs.
For reference, these are the following "expected" SQL drivers that would need
to be imported:
| Database (driver) | Package |
|------------------------------|---------------------------------------------------------------------------------------------|
| Microsoft SQL Server (mssql) | [github.com/denisenkom/go-mssqldb](https://github.com/denisenkom/go-mssqldb) |
| MySQL (mysql) | [github.com/go-sql-driver/mysql](https://github.com/go-sql-driver/mysql) |
| Oracle Database (goracle) | [gopkg.in/goracle.v2](https://gopkg.in/goracle.v2) |
| PostgreSQL (postgres) | [github.com/lib/pq](https://github.com/lib/pq) |
| SQLite3 (sqlite3) | [github.com/mattn/go-sqlite3](https://github.com/mattn/go-sqlite3) |
| | |
| Amazon Redshift (redshift) | [github.com/lib/pq](https://github.com/lib/pq) |
| CockroachDB (cockroachdb) | [github.com/lib/pq](https://github.com/lib/pq) |
| MemSQL (memsql) | [github.com/go-sql-driver/mysql](https://github.com/go-sql-driver/mysql) |
| TiDB (tidb) | [github.com/go-sql-driver/mysql](https://github.com/go-sql-driver/mysql) |
| Vitess (vitess) | [github.com/go-sql-driver/mysql](https://github.com/go-sql-driver/mysql) |
| | |
| Google Spanner (spanner) | github.com/xo/spanner (not yet public) |
| | |
| MySQL (mymysql) | [github.com/ziutek/mymysql/godrv](https://github.com/ziutek/mymysql) |
| PostgreSQL (pgx) | [github.com/jackc/pgx/stdlib](https://github.com/jackc/pgx) |
| | |
| Apache Avatica (avatica) | [github.com/Boostport/avatica](https://github.com/Boostport/avatica) |
| Apache Ignite (ignite) | [github.com/amsokol/ignite-go-client/sql](https://github.com/amsokol/ignite-go-client) |
| Cassandra (cql) | [github.com/MichaelS11/go-cql-driver](https://github.com/MichaelS11/go-cql-driver) |
| ClickHouse (clickhouse) | [github.com/kshvakov/clickhouse](https://github.com/kshvakov/clickhouse) |
| Couchbase (n1ql) | [github.com/couchbase/go_n1ql](https://github.com/couchbase/go_n1ql) |
| Cznic QL (ql) | [github.com/cznic/ql](https://github.com/cznic/ql) |
| Firebird SQL (firebirdsql) | [github.com/nakagami/firebirdsql](https://github.com/nakagami/firebirdsql) |
| Microsoft ADODB (adodb) | [github.com/mattn/go-adodb](https://github.com/mattn/go-adodb) |
| ODBC (odbc) | [github.com/alexbrainman/odbc](https://github.com/alexbrainman/odbc) |
| OLE ODBC (oleodbc) | [github.com/mattn/go-adodb](https://github.com/mattn/go-adodb) |
| Presto (presto) | [github.com/prestodb/presto-go-client/presto](https://github.com/prestodb/presto-go-client) |
| SAP ASE (tds) | [github.com/thda/tds](https://github.com/thda/tds) |
| SAP HANA (hdb) | [github.com/SAP/go-hdb/driver](https://github.com/SAP/go-hdb) |
| Snowflake (snowflake) | [github.com/snowflakedb/gosnowflake](https://github.com/snowflakedb/gosnowflake) |
| VoltDB (voltdb) | [github.com/VoltDB/voltdb-client-go/voltdbclient](github.com/VoltDB/voltdb-client-go]) |
Please see [the `dburl` GoDoc listing][godoc-link] for the full API
documentation.
### URL Parsing Rules
[`dburl.Parse`][godoc-parse] and [`dburl.Open`][godoc-open] rely primarily on
Go's standard [`net/url.URL`][godoc-net-url] type, and as such, parsing or
opening database connection URLs with `dburl` are subject to the same rules,
conventions, and semantics as [Go's `net/url.Parse` func][godoc-net-url-parse].
## Example
A [full example](_example/example.go) for reference:
```go
// _example/example.go
package main
import (
"fmt"
"log"
_ "github.com/denisenkom/go-mssqldb"
"github.com/xo/dburl"
)
func main() {
db, err := dburl.Open("sqlserver://user:pass@localhost/dbname")
if err != nil {
log.Fatal(err)
}
var name string
err = db.QueryRow(`SELECT name FROM mytable WHERE id=10`).Scan(&name)
if err != nil {
log.Fatal(err)
}
fmt.Printf(">> got: %s\n", name)
}
```
## About
`dburl` was built primarily to support these projects:
* [usql][usql] - a universal command-line interface for SQL databases
* [xo][xo] - a command-line tool to generate Go code from a database schema
[go-project]: https://golang.org/project
[godoc-open]: https://godoc.org/github.com/xo/dburl#Open
[godoc-parse]: https://godoc.org/github.com/xo/dburl#Parse
[godoc-sql-db]: https://godoc.org/database/sql#DB
[godoc-net-url]: https://godoc.org/net/url#URL
[godoc-net-url-parse]: https://godoc.org/net/url#URL.Parse
[usql]: https://github.com/xo/usql
[xo]: https://github.com/xo/xo

216
vendor/github.com/xo/dburl/dburl.go generated vendored Normal file
View File

@@ -0,0 +1,216 @@
// Package dburl provides a standard, URL style mechanism for parsing and
// opening SQL database connection strings for Go. Provides standardized way to
// parse and open URLs for popular databases PostgreSQL, MySQL, SQLite3, Oracle
// Database, Microsoft SQL Server, in addition to most other SQL databases with
// a publicly available Go driver.
//
// Database Connection URL Overview
//
// Supported database connection URLs are of the form:
//
// protocol+transport://user:pass@host/dbname?opt1=a&opt2=b
// protocol:/path/to/file
//
// Where:
//
// protocol - driver name or alias (see below)
// transport - "tcp", "udp", "unix" or driver name (odbc/oleodbc) |
// user - username
// pass - password
// host - host
// dbname* - database, instance, or service name/id to connect to
// ?opt1=... - additional database driver options
// (see respective SQL driver for available options)
//
// * for Microsoft SQL Server, /dbname can be /instance/dbname, where /instance
// is optional. For Oracle Database, /dbname is of the form /service/dbname
// where /service is the service name or SID, and /dbname is optional. Please
// see below for examples.
//
// Quickstart
//
// Database connection URLs in the above format can be parsed with Parse as such:
//
// import (
// "github.com/xo/dburl"
// )
// u, err := dburl.Parse("postgresql://user:pass@localhost/mydatabase/?sslmode=disable")
// if err != nil { /* ... */ }
//
// Additionally, a simple helper, Open, is provided that will parse, open, and
// return a standard sql.DB database connection:
//
// import (
// "github.com/xo/dburl"
// )
// db, err := dburl.Open("sqlite:mydatabase.sqlite3?loc=auto")
// if err != nil { /* ... */ }
//
// Example URLs
//
// The following are example database connection URLs that can be handled by
// Parse and Open:
//
// postgres://user:pass@localhost/dbname
// pg://user:pass@localhost/dbname?sslmode=disable
// mysql://user:pass@localhost/dbname
// mysql:/var/run/mysqld/mysqld.sock
// sqlserver://user:pass@remote-host.com/dbname
// mssql://user:pass@remote-host.com/instance/dbname
// ms://user:pass@remote-host.com:port/instance/dbname?keepAlive=10
// oracle://user:pass@somehost.com/sid
// sap://user:pass@localhost/dbname
// sqlite:/path/to/file.db
// file:myfile.sqlite3?loc=auto
// odbc+postgres://user:pass@localhost:port/dbname?option1=
//
// Protocol Schemes and Aliases
//
// The following protocols schemes (ie, driver) and their associated aliases
// are supported out of the box:
//
// Database (scheme/driver) | Protocol Aliases [real driver]
// -----------------------------|--------------------------------------------
// Microsoft SQL Server (mssql) | ms, sqlserver
// MySQL (mysql) | my, mariadb, maria, percona, aurora
// Oracle Database (goracle) | or, ora, goracle, oci, oci8, odpi, odpi-c
// PostgreSQL (postgres) | pg, postgresql, pgsql
// SQLite3 (sqlite3) | sq, sqlite, file
// -----------------------------|--------------------------------------------
// Amazon Redshift (redshift) | rs [postgres]
// CockroachDB (cockroachdb) | cr, cockroach, crdb, cdb [postgres]
// MemSQL (memsql) | me [mysql]
// TiDB (tidb) | ti [mysql]
// Vitess (vitess) | vt [mysql]
// -----------------------------|--------------------------------------------
// Google Spanner (spanner) | gs, google, span (not yet public)
// -----------------------------|--------------------------------------------
// MySQL (mymysql) | zm, mymy
// PostgreSQL (pgx) | px
// -----------------------------|--------------------------------------------
// Apache Avatica (avatica) | av, phoenix
// Apache Ignite (ignite) | ig, gridgain
// Cassandra (cql) | ca, cassandra, datastax, scy, scylla
// ClickHouse (clickhouse) | ch
// Couchbase (n1ql) | n1, couchbase
// Cznic QL (ql) | ql, cznic, cznicql
// Firebird SQL (firebirdsql) | fb, firebird
// Microsoft ADODB (adodb) | ad, ado
// ODBC (odbc) | od
// OLE ODBC (oleodbc) | oo, ole, oleodbc [adodb]
// Presto (presto) | pr, prestodb, prestos, prs, prestodbs
// SAP ASE (tds) | ax, ase, sapase
// SAP HANA (hdb) | sa, saphana, sap, hana
// Snowflake (snowflake) | sf
// VoltDB (voltdb) | vo, volt, vdb
//
// Any protocol scheme alias:// can be used in place of protocol://, and will
// work identically with Parse and Open.
//
// Using
//
// Please note that the dburl package does not import actual SQL drivers, and
// only provides a standard way to parse/open respective database connection URLs.
//
// For reference, these are the following "expected" SQL drivers that would need
// to be imported:
//
// Database (scheme/driver) | Package
// -----------------------------|-------------------------------------------------
// Microsoft SQL Server (mssql) | github.com/denisenkom/go-mssqldb
// MySQL (mysql) | github.com/go-sql-driver/mysql
// Oracle Database (goracle) | gopkg.in/goracle.v2
// PostgreSQL (postgres) | github.com/lib/pq
// SQLite3 (sqlite3) | github.com/mattn/go-sqlite3
// -----------------------------|-------------------------------------------------
// Amazon Redshift (redshift) | github.com/lib/pq
// CockroachDB (cockroachdb) | github.com/lib/pq
// MemSQL (memsql) | github.com/go-sql-driver/mysql
// TiDB (tidb) | github.com/go-sql-driver/mysql
// Vitess (vitess) | github.com/go-sql-driver/mysql
// -----------------------------|-------------------------------------------------
// Google Spanner (spanner) | github.com/xo/spanner (not yet public)
// -----------------------------|-------------------------------------------------
// MySQL (mymysql) | github.com/ziutek/mymysql/godrv
// PostgreSQL (pgx) | github.com/jackc/pgx/stdlib
// -----------------------------|-------------------------------------------------
// Apache Avatica (avatica) | github.com/Boostport/avatica
// Apache Ignite (ignite) | github.com/amsokol/ignite-go-client/sql
// Cassandra (cql) | github.com/MichaelS11/go-cql-driver
// ClickHouse (clickhouse) | github.com/kshvakov/clickhouse
// Couchbase (n1ql) | github.com/couchbase/go_n1ql
// Cznic QL (ql) | github.com/cznic/ql
// Firebird SQL (firebirdsql) | github.com/nakagami/firebirdsql
// Microsoft ADODB (adodb) | github.com/mattn/go-adodb
// ODBC (odbc) | github.com/alexbrainman/odbc
// OLE ODBC (oleodbc)* | github.com/mattn/go-adodb
// Presto (presto) | github.com/prestodb/presto-go-client
// SAP ASE (tds) | github.com/thda/tds
// SAP HANA (hdb) | github.com/SAP/go-hdb/driver
// Snowflake (snowflake) | github.com/snowflakedb/gosnowflake
// VoltDB (voltdb) | github.com/VoltDB/voltdb-client-go/voltdbclient
//
// * OLE ODBC is a special alias for using the "MSDASQL.1" OLE provider with the
// ADODB driver on Windows. oleodbc:// URLs will be converted to the equivalent
// ADODB DSN with "Extended Properties" having the respective ODBC parameters,
// including the underlying transport prootocol. As such, oleodbc+protocol://user:pass@host/dbname
// URLs are equivalent to adodb://MSDASQL.1/?Extended+Properties=.... on
// Windows. See GenOLEODBC for information regarding how URL components are
// mapped and passed to ADODB's Extended Properties parameter.
//
// URL Parsing Rules
//
// Parse and Open rely heavily on the standard net/url.URL type, as such
// parsing rules have the same conventions/semantics as any URL parsed by the
// standard library's net/url.Parse.
//
// Related Projects
//
// This package was written mainly to support xo (https://github.com/xo/xo)
// and usql (https://github.com/xo/usql).
package dburl
import (
"database/sql"
)
// Error is a dburl error.
type Error string
// Error satisfies the error interface.
func (err Error) Error() string {
return string(err)
}
const (
// ErrInvalidDatabaseScheme is the invalid database scheme error.
ErrInvalidDatabaseScheme Error = "invalid database scheme"
// ErrUnknownDatabaseScheme is the unknown database type error.
ErrUnknownDatabaseScheme Error = "unknown database scheme"
// ErrInvalidTransportProtocol is the invalid transport protocol error.
ErrInvalidTransportProtocol Error = "invalid transport protocol"
// ErrRelativePathNotSupported is the relative paths not supported error.
ErrRelativePathNotSupported Error = "relative path not supported"
// ErrMissingHost is the missing host error.
ErrMissingHost Error = "missing host"
// ErrMissingPath is the missing path error.
ErrMissingPath Error = "missing path"
)
// Open takes a urlstr like "protocol+transport://user:pass@host/dbname?option1=a&option2=b"
// and creates a standard sql.DB connection.
//
// See Parse for information on formatting URLs to work properly with Open.
func Open(urlstr string) (*sql.DB, error) {
u, err := Parse(urlstr)
if err != nil {
return nil, err
}
return sql.Open(u.Driver, u.DSN)
}

587
vendor/github.com/xo/dburl/dsn.go generated vendored Normal file
View File

@@ -0,0 +1,587 @@
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
}
// 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 goracle DSN from the passed URL.
func GenOracle(u *URL) (string, error) {
// Easy Connect Naming method enables clients to connect to a database server
// without any configuration. Clients use a connect string for a simple TCP/IP
// address, which includes a host name and optional port and service name:
// CONNECT username[/password]@[//]host[:port][/service_name][:server][/instance_name]
host, port, service := hostname(u.Host), hostport(u.Host), strings.TrimPrefix(u.Path, "/")
var instance string
// grab instance name from service name
if i := strings.LastIndex(service, "/"); i != -1 {
instance = service[i+1:]
service = service[:i]
}
// build dsn
dsn := host
if port != "" {
dsn += ":" + port
}
// 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 + "@//" + dsn
}
}
if service != "" {
dsn += "/" + service
}
if instance != "" {
dsn += "/" + instance
}
return 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
}

3
vendor/github.com/xo/dburl/go.mod generated vendored Normal file
View File

@@ -0,0 +1,3 @@
module github.com/xo/dburl
go 1.13

250
vendor/github.com/xo/dburl/scheme.go generated vendored Normal file
View File

@@ -0,0 +1,250 @@
package dburl
import (
"fmt"
"sort"
)
// Proto are the allowed transport protocol types in a database URL scheme.
type Proto uint
// Proto types.
const (
ProtoNone Proto = 0
ProtoTCP Proto = 1
ProtoUDP Proto = 2
ProtoUnix Proto = 4
ProtoAny Proto = 8
)
// Scheme wraps information used for registering a URL scheme with
// Parse/Open.
type Scheme struct {
// Driver is the name of the SQL driver that will set as the Scheme in
// Parse'd URLs, and is the driver name expected by the standard sql.Open
// calls.
//
// Note: a 2 letter alias will always be registered for the Driver as the
// first 2 characters of the Driver, unless one of the Aliases includes an
// alias that is 2 characters.
Driver string
// Generator is the func responsible for generating a DSN based on parsed
// URL information.
//
// Note: this func should not modify the passed URL.
Generator func(*URL) (string, error)
// Proto are allowed protocol types for the scheme.
Proto Proto
// Opaque toggles Parse to not re-process URLs with an "opaque" component.
Opaque bool
// Aliases are any additional aliases for the scheme.
Aliases []string
// Override is the Go SQL driver to use instead of Driver.
Override string
}
// BaseSchemes returns the supported base schemes.
func BaseSchemes() []Scheme {
return []Scheme{
// core databases
{"mssql", GenSQLServer, 0, false, []string{"sqlserver"}, ""},
{"mysql", GenMySQL, ProtoTCP | ProtoUDP | ProtoUnix, false, []string{"mariadb", "maria", "percona", "aurora"}, ""},
{"goracle", GenOracle, 0, false, []string{"or", "ora", "oracle", "oci", "oci8", "odpi", "odpi-c"}, ""},
{"postgres", GenPostgres, ProtoUnix, false, []string{"pg", "postgresql", "pgsql"}, ""},
{"sqlite3", GenOpaque, 0, true, []string{"sqlite", "file"}, ""},
// wire compatibles
{"cockroachdb", GenFromURL("postgres://localhost:26257/?sslmode=disable"), 0, false, []string{"cr", "cockroach", "crdb", "cdb"}, "postgres"},
{"memsql", GenMySQL, 0, false, nil, "mysql"},
{"redshift", GenFromURL("postgres://localhost:5439/"), 0, false, []string{"rs"}, "postgres"},
{"tidb", GenMySQL, 0, false, nil, "mysql"},
{"vitess", GenMySQL, 0, false, []string{"vt"}, "mysql"},
// testing
{"spanner", GenScheme("spanner"), 0, false, []string{"gs", "google", "span"}, ""},
// alternate implementations
{"mymysql", GenMyMySQL, ProtoTCP | ProtoUDP | ProtoUnix, false, []string{"zm", "mymy"}, ""},
{"pgx", GenScheme("postgres"), ProtoUnix, false, []string{"px"}, ""},
// other databases
{"adodb", GenADODB, 0, false, []string{"ado"}, ""},
{"avatica", GenFromURL("http://localhost:8765/"), 0, false, []string{"phoenix"}, ""},
{"cql", GenCassandra, 0, false, []string{"ca", "cassandra", "datastax", "scy", "scylla"}, ""},
{"clickhouse", GenClickhouse, 0, false, []string{"ch"}, ""},
{"firebirdsql", GenFirebird, 0, false, []string{"fb", "firebird"}, ""},
{"hdb", GenScheme("hdb"), 0, false, []string{"sa", "saphana", "sap", "hana"}, ""},
{"ignite", GenIgnite, 0, false, []string{"ig", "gridgain"}, ""},
{"n1ql", GenFromURL("http://localhost:9000/"), 0, false, []string{"couchbase"}, ""},
{"odbc", GenODBC, ProtoAny, false, nil, ""},
{"oleodbc", GenOLEODBC, ProtoAny, false, []string{"oo", "ole"}, "adodb"},
{"presto", GenPresto, 0, false, []string{"prestodb", "prestos", "prs", "prestodbs"}, ""},
{"ql", GenOpaque, 0, true, []string{"ql", "cznic", "cznicql"}, ""},
{"snowflake", GenSnowflake, 0, false, []string{"sf"}, ""},
{"tds", GenFromURL("http://localhost:5000/"), 0, false, []string{"ax", "ase", "sapase"}, ""},
{"voltdb", GenVoltDB, 0, false, []string{"volt", "vdb"}, ""},
}
}
func init() {
schemes := BaseSchemes()
schemeMap = make(map[string]*Scheme, len(schemes))
// register
for _, scheme := range schemes {
Register(scheme)
}
}
// schemeMap is the map of registered schemes.
var schemeMap map[string]*Scheme
// registerAlias registers a alias for an already registered Scheme.
func registerAlias(name, alias string, doSort bool) {
scheme, ok := schemeMap[name]
if !ok {
panic(fmt.Sprintf("scheme %s not registered", name))
}
if doSort && has(scheme.Aliases, alias) {
panic(fmt.Sprintf("scheme %s already has alias %s", name, alias))
}
if _, ok := schemeMap[alias]; ok {
panic(fmt.Sprintf("scheme %s already registered", alias))
}
scheme.Aliases = append(scheme.Aliases, alias)
if doSort {
sort.Sort(ss(scheme.Aliases))
}
schemeMap[alias] = scheme
}
// Register registers a Scheme.
func Register(scheme Scheme) {
if scheme.Generator == nil {
panic("must specify Generator when registering Scheme")
}
if scheme.Opaque && scheme.Proto&ProtoUnix != 0 {
panic("scheme must support only Opaque or Unix protocols, not both")
}
// register
if _, ok := schemeMap[scheme.Driver]; ok {
panic(fmt.Sprintf("scheme %s already registered", scheme.Driver))
}
sz := &Scheme{
Driver: scheme.Driver,
Generator: scheme.Generator,
Proto: scheme.Proto,
Opaque: scheme.Opaque,
Override: scheme.Override,
}
schemeMap[scheme.Driver] = sz
// add aliases
var hasShort bool
for _, alias := range scheme.Aliases {
if len(alias) == 2 {
hasShort = true
}
if scheme.Driver != alias {
registerAlias(scheme.Driver, alias, false)
}
}
if !hasShort && len(scheme.Driver) > 2 {
registerAlias(scheme.Driver, scheme.Driver[:2], false)
}
// ensure always at least one alias, and that if Driver is 2 characters,
// that it gets added as well
if len(sz.Aliases) == 0 || len(scheme.Driver) == 2 {
sz.Aliases = append(sz.Aliases, scheme.Driver)
}
// sort
sort.Sort(ss(sz.Aliases))
}
// Unregister unregisters a Scheme and all associated aliases.
func Unregister(name string) *Scheme {
scheme, ok := schemeMap[name]
if ok {
for _, alias := range scheme.Aliases {
delete(schemeMap, alias)
}
delete(schemeMap, name)
return scheme
}
return nil
}
// RegisterAlias registers a alias for an already registered Scheme.h
func RegisterAlias(name, alias string) {
registerAlias(name, alias, true)
}
// has is a util func to determine if a contains b.
func has(a []string, b string) bool {
for _, s := range a {
if s == b {
return true
}
}
return false
}
// SchemeDriverAndAliases returns the registered driver and aliases for a
// database scheme.
func SchemeDriverAndAliases(name string) (string, []string) {
if scheme, ok := schemeMap[name]; ok {
driver := scheme.Driver
if scheme.Override != "" {
driver = scheme.Override
}
var aliases []string
for _, alias := range scheme.Aliases {
if alias == driver {
continue
}
aliases = append(aliases, alias)
}
sort.Sort(ss(aliases))
return driver, aliases
}
return "", nil
}
// ss is a util type to provide sorting of a string slice (used for sorting
// aliases).
type ss []string
func (s ss) Len() int { return len(s) }
func (s ss) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s ss) Less(i, j int) bool {
if len(s[i]) <= len(s[j]) {
return true
}
if len(s[j]) < len(s[i]) {
return false
}
return s[i] < s[j]
}

235
vendor/github.com/xo/dburl/url.go generated vendored Normal file
View File

@@ -0,0 +1,235 @@
package dburl
import (
"net/url"
"strings"
)
// URL wraps the standard net/url.URL type, adding OriginalScheme, Proto,
// Driver, and DSN strings.
type URL struct {
// URL is the base net/url/URL.
url.URL
// OriginalScheme is the original parsed scheme (ie, "sq", "mysql+unix", "sap", etc).
OriginalScheme string
// Proto is the specified protocol (ie, "tcp", "udp", "unix"), if provided.
Proto string
// Driver is the non-aliased SQL driver name that should be used in a call
// to sql/Open.
Driver string
// Unaliased is the unaliased driver name.
Unaliased string
// DSN is the built connection "data source name" that can be used in a
// call to sql/Open.
DSN string
// hostPortDB will be set by Gen*() funcs after determining the host, port,
// database.
//
// when empty, indicates that these values are not special, and can be
// retrieved as the host, port, and path[1:] as usual.
hostPortDB []string
}
// Parse parses urlstr, returning a URL with the OriginalScheme, Proto, Driver,
// Unaliased, and DSN fields populated.
//
// Note: if urlstr has a Opaque component (ie, URLs not specified as "scheme://"
// but "scheme:"), and the database scheme does not support opaque components,
// then Parse will attempt to re-process the URL as "scheme://<opaque>" using
// the OriginalScheme.
func Parse(urlstr string) (*URL, error) {
// parse url
u, err := url.Parse(urlstr)
if err != nil {
return nil, err
}
if u.Scheme == "" {
return nil, ErrInvalidDatabaseScheme
}
// create url
v := &URL{URL: *u, OriginalScheme: urlstr[:len(u.Scheme)], Proto: "tcp"}
// check for +protocol in scheme
var checkProto bool
if i := strings.IndexRune(v.Scheme, '+'); i != -1 {
v.Proto = urlstr[i+1 : len(u.Scheme)]
v.Scheme = v.Scheme[:i]
checkProto = true
}
// get dsn generator
scheme, ok := schemeMap[v.Scheme]
if !ok {
return nil, ErrUnknownDatabaseScheme
}
// if scheme does not understand opaque URLs, retry parsing after making a fully
// qualified URL
if !scheme.Opaque && v.Opaque != "" {
q := ""
if v.RawQuery != "" {
q = "?" + v.RawQuery
}
f := ""
if v.Fragment != "" {
f = "#" + v.Fragment
}
return Parse(v.OriginalScheme + "://" + v.Opaque + q + f)
}
if scheme.Opaque && v.Opaque == "" {
// force Opaque
v.Opaque, v.Host, v.Path, v.RawPath = v.Host+v.Path, "", "", ""
} else if v.Host == "." || (v.Host == "" && strings.TrimPrefix(v.Path, "/") != "") {
// force unix proto
v.Proto = "unix"
}
// check proto
if checkProto || v.Proto != "tcp" {
if scheme.Proto == ProtoNone {
return nil, ErrInvalidTransportProtocol
}
switch {
case scheme.Proto&ProtoAny != 0 && v.Proto != "":
case scheme.Proto&ProtoTCP != 0 && v.Proto == "tcp":
case scheme.Proto&ProtoUDP != 0 && v.Proto == "udp":
case scheme.Proto&ProtoUnix != 0 && v.Proto == "unix":
default:
return nil, ErrInvalidTransportProtocol
}
}
// set driver
v.Driver = scheme.Driver
v.Unaliased = scheme.Driver
if scheme.Override != "" {
v.Driver = scheme.Override
}
// generate dsn
v.DSN, err = scheme.Generator(v)
if err != nil {
return nil, err
}
return v, nil
}
// String satisfies the stringer interface.
func (u *URL) String() string {
p := &url.URL{
Scheme: u.OriginalScheme,
Opaque: u.Opaque,
User: u.User,
Host: u.Host,
Path: u.Path,
RawPath: u.RawPath,
RawQuery: u.RawQuery,
Fragment: u.Fragment,
}
return p.String()
}
// Short provides a short description of the user, host, and database.
func (u *URL) Short() string {
if u.Scheme == "" {
return ""
}
s := schemeMap[u.Scheme].Aliases[0]
if u.Scheme == "odbc" || u.Scheme == "oleodbc" {
n := u.Proto
if v, ok := schemeMap[n]; ok {
n = v.Aliases[0]
}
s += "+" + n
} else if u.Proto != "tcp" {
s += "+" + u.Proto
}
s += ":"
if u.User != nil {
if un := u.User.Username(); un != "" {
s += un + "@"
}
}
if u.Host != "" {
s += u.Host
}
if u.Path != "" && u.Path != "/" {
s += u.Path
}
if u.Opaque != "" {
s += u.Opaque
}
return s
}
// Normalize returns the driver, host, port, database, and user name of a URL,
// joined with sep, populating blank fields with empty.
func (u *URL) Normalize(sep, empty string, cut int) string {
s := make([]string, 5)
s[0] = u.Unaliased
if u.Proto != "tcp" && u.Proto != "unix" {
s[0] += "+" + u.Proto
}
// set host port dbname fields
if u.hostPortDB == nil {
if u.Opaque != "" {
u.hostPortDB = []string{u.Opaque, "", ""}
} else {
u.hostPortDB = []string{
hostname(u.Host),
hostport(u.Host),
strings.TrimPrefix(u.Path, "/"),
}
}
}
copy(s[1:], u.hostPortDB)
// set user
if u.User != nil {
s[4] = u.User.Username()
}
// replace blank entries ...
for i := 0; i < len(s); i++ {
if s[i] == "" {
s[i] = empty
}
}
if cut > 0 {
// cut to only populated fields
i := len(s) - 1
for ; i > cut; i-- {
if s[i] != "" {
break
}
}
s = s[:i]
}
// join
return strings.Join(s, sep)
}

179
vendor/github.com/xo/dburl/util.go generated vendored Normal file
View File

@@ -0,0 +1,179 @@
package dburl
import (
"net/url"
"os"
stdpath "path"
"sort"
"strings"
)
// contains code taken from go1.8, for purposes of backwards compatability with
// older go versions.
// hostname returns u.Host, without any port number.
//
// If Host is an IPv6 literal with a port number, Hostname returns the
// IPv6 literal without the square brackets. IPv6 literals may include
// a zone identifier.
func hostname(hostport string) string {
colon := strings.IndexByte(hostport, ':')
if colon == -1 {
return hostport
}
if i := strings.IndexByte(hostport, ']'); i != -1 {
return strings.TrimPrefix(hostport[:i], "[")
}
return hostport[:colon]
}
// hostport returns the port part of u.Host, without the leading colon.
// If u.Host doesn't contain a port, Port returns an empty string.
func hostport(hostport string) string {
colon := strings.IndexByte(hostport, ':')
if colon == -1 {
return ""
}
if i := strings.Index(hostport, "]:"); i != -1 {
return hostport[i+len("]:"):]
}
if strings.Contains(hostport, "]") {
return ""
}
return hostport[colon+len(":"):]
}
// genOptions takes URL values and generates options, joining together with
// joiner, and separated by sep, with any multi URL values joined by valSep,
// ignoring any values with keys in ignore.
//
// For example, to build a "ODBC" style connection string, use like the following:
// genOptions(u.Query(), "", "=", ";", ",")
func genOptions(q url.Values, joiner, assign, sep, valSep string, skipWhenEmpty bool, ignore ...string) string {
qlen := len(q)
if qlen == 0 {
return ""
}
// make ignore map
ig := make(map[string]bool, len(ignore))
for _, v := range ignore {
ig[strings.ToLower(v)] = true
}
// sort keys
s := make([]string, len(q))
var i int
for k := range q {
s[i] = k
i++
}
sort.Strings(s)
var opts []string
for _, k := range s {
if !ig[strings.ToLower(k)] {
val := strings.Join(q[k], valSep)
if !skipWhenEmpty || val != "" {
if val != "" {
val = assign + val
}
opts = append(opts, k+val)
}
}
}
if len(opts) != 0 {
return joiner + strings.Join(opts, sep)
}
return ""
}
// genOptionsODBC is a util wrapper around genOptions that uses the fixed settings
// for ODBC style connection strings.
func genOptionsODBC(q url.Values, skipWhenEmpty bool, ignore ...string) string {
return genOptions(q, "", "=", ";", ",", skipWhenEmpty, ignore...)
}
// genQueryOptions generates standard query options.
func genQueryOptions(q url.Values) string {
if s := q.Encode(); s != "" {
return "?" + s
}
return ""
}
// convertOptions converts an option value based on name, value pairs.
func convertOptions(q url.Values, pairs ...string) url.Values {
n := make(url.Values)
for k, v := range q {
x := make([]string, len(v))
for i, z := range v {
for j := 0; j < len(pairs); j += 2 {
if pairs[j] == z {
z = pairs[j+1]
}
}
x[i] = z
}
n[k] = x
}
return n
}
// mode returns the mode of the path.
func mode(path string) os.FileMode {
if fi, err := os.Stat(path); err == nil {
return fi.Mode()
}
return 0
}
// resolveSocket tries to resolve a path to a Unix domain socket based on the
// form "/path/to/socket/dbname" returning either the original path and the
// empty string, or the components "/path/to/socket" and "dbname", when
// /path/to/socket/dbname is reported by os.Stat as a socket.
//
// Used for MySQL DSNs.
func resolveSocket(path string) (string, string) {
dir, dbname := path, ""
for dir != "" && dir != "/" && dir != "." {
if m := mode(dir); m&os.ModeSocket != 0 {
return dir, dbname
}
dir, dbname = stdpath.Dir(dir), stdpath.Base(dir)
}
return path, ""
}
// resolveDir resolves a directory with a :port list.
//
// Used for PostgreSQL DSNs.
func resolveDir(path string) (string, string, string) {
dir := path
for dir != "" && dir != "/" && dir != "." {
port := ""
i, j := strings.LastIndex(dir, ":"), strings.LastIndex(dir, "/")
if i != -1 && i > j {
port = dir[i+1:]
dir = dir[:i]
}
if mode(dir)&os.ModeDir != 0 {
rest := strings.TrimPrefix(strings.TrimPrefix(strings.TrimPrefix(path, dir), ":"+port), "/")
return dir, port, rest
}
if j != -1 {
dir = dir[:j]
} else {
dir = ""
}
}
return path, "", ""
}