func (p ConnParams) ConnString() string {
	var kvs []string
	escaper := strings.NewReplacer(` `, `\ `, `'`, `\'`, `\`, `\\`)
	for k, v := range p {
		if v != "" {
			kvs = append(kvs, k+"="+escaper.Replace(v))
		}
	}
	sort.Strings(kvs)
	return strings.Join(kvs, " ")
}
ConnString returns a connection string, its entries are sorted so the
returned string can be reproducible and comparable
package postgresql
import (
	"fmt"
	"net/url"
	"reflect"
	"sort"
	"strings"
	"unicode"
)
type ConnParams map[string]string
func (cp ConnParams) Set(k, v string) {
	cp[k] = v
}
func (cp ConnParams) Get(k string) (v string) {
	return cp[k]
}
func (cp ConnParams) Del(k string) {
	delete(cp, k)
}
func (cp ConnParams) Isset(k string) bool {
	_, ok := cp[k]
	return ok
}
func (cp ConnParams) Equals(cp2 ConnParams) bool {
	return reflect.DeepEqual(cp, cp2)
}
func (cp ConnParams) Copy() ConnParams {
	ncp := ConnParams{}
	for k, v := range cp {
		ncp[k] = v
	}
	return ncp
}
type scanner struct {
	s []rune
	i int
}
func newScanner(s string) *scanner {
	return &scanner{[]rune(s), 0}
}
func (s *scanner) Next() (rune, bool) {
	if s.i >= len(s.s) {
		return 0, false
	}
	r := s.s[s.i]
	s.i++
	return r, true
}
func (s *scanner) SkipSpaces() (rune, bool) {
	r, ok := s.Next()
	for unicode.IsSpace(r) && ok {
		r, ok = s.Next()
	}
	return r, ok
}
func ParseConnString(name string) (ConnParams, error) {
	p := make(ConnParams)
	s := newScanner(name)
	for {
		var (
			keyRunes, valRunes []rune
			r                  rune
			ok                 bool
		)
		if r, ok = s.SkipSpaces(); !ok {
			break
		}
		for !unicode.IsSpace(r) && r != '=' {
			keyRunes = append(keyRunes, r)
			if r, ok = s.Next(); !ok {
				break
			}
		}
		if r != '=' {
			r, ok = s.SkipSpaces()
		}
		if r != '=' || !ok {
			return nil, fmt.Errorf(`missing "=" after %q in connection info string"`, string(keyRunes))
		}
		// Skip any whitespace after the =
		if r, ok = s.SkipSpaces(); !ok {
			// If we reach the end here, the last value is just an empty string as per libpq.
			p.Set(string(keyRunes), "")
			break
		}
		if r != '\'' {
			for !unicode.IsSpace(r) {
				if r == '\\' {
					if r, ok = s.Next(); !ok {
						return nil, fmt.Errorf(`missing character after backslash`)
					}
				}
				valRunes = append(valRunes, r)
				if r, ok = s.Next(); !ok {
					break
				}
			}
		} else {
		quote:
			for {
				if r, ok = s.Next(); !ok {
					return nil, fmt.Errorf(`unterminated quoted string literal in connection string`)
				}
				switch r {
				case '\'':
					break quote
				case '\\':
					r, _ = s.Next()
					fallthrough
				default:
					valRunes = append(valRunes, r)
				}
			}
		}
		p.Set(string(keyRunes), string(valRunes))
	}
	return p, nil
}
// URLToConnParams creates the connParams from the url.
func URLToConnParams(urlStr string) (ConnParams, error) {
	p := make(ConnParams)
	u, err := url.Parse(urlStr)
	if err != nil {
		return nil, err
	}
	if u.Scheme != "postgres" {
		return nil, fmt.Errorf("invalid connection protocol: %s", u.Scheme)
	}
	if u.User != nil {
		v := u.User.Username()
		p.Set("user", v)
		v, _ = u.User.Password()
		p.Set("password", v)
	}
	i := strings.Index(u.Host, ":")
	if i < 0 {
		p.Set("host", u.Host)
	} else {
		p.Set("host", u.Host[:i])
		p.Set("port", u.Host[i+1:])
	}
	if u.Path != "" {
		p.Set("dbname", u.Path[1:])
	}
	q := u.Query()
	for k := range q {
		p.Set(k, q.Get(k))
	}
	return p, nil
}