github.com/thiagoyeds/go-cloud@v0.26.0/mysql/mysql.go (about) 1 // Copyright 2019 The Go Cloud Development Kit Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package mysql provides functions to open MySQL databases with OpenCensus instrumentation. 16 package mysql 17 18 import ( 19 "context" 20 "database/sql" 21 "database/sql/driver" 22 "fmt" 23 "net/url" 24 "regexp" 25 "strings" 26 27 "github.com/go-sql-driver/mysql" 28 "gocloud.dev/internal/openurl" 29 30 "contrib.go.opencensus.io/integrations/ocsql" 31 ) 32 33 // Scheme is the URL scheme this package registers its URLOpener under on 34 // DefaultMux. 35 const Scheme = "mysql" 36 37 func init() { 38 DefaultURLMux().RegisterMySQL(Scheme, &URLOpener{}) 39 } 40 41 // URLOpener opens URLs like "mysql://" by using the underlying MySQL driver. 42 // like "mysql://user:password@localhost:3306/mydb". 43 type URLOpener struct { 44 TraceOpts []ocsql.TraceOption 45 } 46 47 // OpenMySQLURL opens a new database connection wrapped with OpenCensus instrumentation. 48 func (uo *URLOpener) OpenMySQLURL(_ context.Context, u *url.URL) (*sql.DB, error) { 49 cfg, err := ConfigFromURL(u) 50 if err != nil { 51 return nil, err 52 } 53 return sql.OpenDB(connector{ 54 dsn: cfg.FormatDSN(), 55 traceOpts: append([]ocsql.TraceOption(nil), uo.TraceOpts...), 56 }), nil 57 } 58 59 var netAddrRE = regexp.MustCompile(`^(.+)\((.+)\)$`) 60 61 // ConfigFromURL creates a mysql.Config from URL. 62 func ConfigFromURL(u *url.URL) (cfg *mysql.Config, err error) { 63 dbName := strings.TrimPrefix(u.Path, "/") 64 if u.RawQuery != "" { 65 optDsn := fmt.Sprintf("/%s?%s", dbName, u.RawQuery) 66 if cfg, err = mysql.ParseDSN(optDsn); err != nil { 67 return nil, err 68 } 69 } else { 70 cfg = mysql.NewConfig() 71 } 72 if matches := netAddrRE.FindStringSubmatch(u.Host); len(matches) == 3 { 73 cfg.Net = matches[1] 74 cfg.Addr = matches[2] 75 } else { 76 cfg.Net = "tcp" 77 cfg.Addr = u.Host 78 } 79 cfg.User = u.User.Username() 80 cfg.Passwd, _ = u.User.Password() 81 cfg.DBName = dbName 82 cfg.AllowCleartextPasswords = true 83 cfg.AllowNativePasswords = true 84 return cfg, nil 85 } 86 87 type connector struct { 88 dsn string 89 traceOpts []ocsql.TraceOption 90 } 91 92 func (c connector) Connect(context.Context) (driver.Conn, error) { 93 return c.Driver().Open(c.dsn) 94 } 95 96 func (c connector) Driver() driver.Driver { 97 return ocsql.Wrap(mysql.MySQLDriver{}, c.traceOpts...) 98 } 99 100 // A type that implements MySQLURLOpener can open connection based on a URL. 101 // The opener must not modify the URL argument. OpenMySQLURL must be safe to 102 // call from multiple goroutines. 103 // 104 // This interface is generally implemented by types in driver packages. 105 type MySQLURLOpener interface { 106 OpenMySQLURL(ctx context.Context, u *url.URL) (*sql.DB, error) 107 } 108 109 // URLMux is a URL opener multiplexer. It matches the scheme of the URLs 110 // against a set of registered schemes and calls the opener that matches the 111 // URL's scheme. 112 // 113 // The zero value is a multiplexer with no registered schemes. 114 type URLMux struct { 115 schemes openurl.SchemeMap 116 } 117 118 // RegisterMySQL registers the opener with the given scheme. If an opener 119 // already exists for the scheme, RegisterMySQL panics. 120 func (mux *URLMux) RegisterMySQL(scheme string, opener MySQLURLOpener) { 121 mux.schemes.Register("mysql", "DB", scheme, opener) 122 } 123 124 // OpenMySQL calls OpenMySQLURL with the URL parsed from urlstr. 125 // OpenMySQL is safe to call from multiple goroutines. 126 func (mux *URLMux) OpenMySQL(ctx context.Context, urlstr string) (*sql.DB, error) { 127 opener, u, err := mux.schemes.FromString("DB", urlstr) 128 if err != nil { 129 return nil, err 130 } 131 return opener.(MySQLURLOpener).OpenMySQLURL(ctx, u) 132 } 133 134 // OpenMySQLURL dispatches the URL to the opener that is registered with the 135 // URL's scheme. OpenMySQLURL is safe to call from multiple goroutines. 136 func (mux *URLMux) OpenMySQLURL(ctx context.Context, u *url.URL) (*sql.DB, error) { 137 opener, err := mux.schemes.FromURL("DB", u) 138 if err != nil { 139 return nil, err 140 } 141 return opener.(MySQLURLOpener).OpenMySQLURL(ctx, u) 142 } 143 144 var defaultURLMux = new(URLMux) 145 146 // DefaultURLMux returns the URLMux used by OpenMySql. 147 // 148 // Driver packages can use this to register their MySQLURLOpener on the mux. 149 func DefaultURLMux() *URLMux { 150 return defaultURLMux 151 } 152 153 // Open opens the bucket identified by the URL given. URL openers must be 154 // registered in the DefaultURLMux, which is typically done in driver 155 // packages' initialization. 156 // 157 // See the URLOpener documentation in driver subpackages for more 158 // details on supported scheme(s) and URL parameter(s). 159 func Open(ctx context.Context, urlstr string) (*sql.DB, error) { 160 return defaultURLMux.OpenMySQL(ctx, urlstr) 161 }