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  }