github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/go-sql-driver/mysql/driver.go (about)

     1  // Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved.
     2  //
     3  // This Source Code Form is subject to the terms of the Mozilla Public
     4  // License, v. 2.0. If a copy of the MPL was not distributed with this file,
     5  // You can obtain one at http://mozilla.org/MPL/2.0/.
     6  
     7  // Package mysql provides a MySQL driver for Go's database/sql package
     8  //
     9  // The driver should be used via the database/sql package:
    10  //
    11  //  import "database/sql"
    12  //  import _ "github.com/insionng/yougam/libraries/go-sql-driver/mysql"
    13  //
    14  //  db, err := sql.Open("mysql", "user:password@/dbname")
    15  //
    16  // See https://yougam/libraries/go-sql-driver/mysql#usage for details
    17  package mysql
    18  
    19  import (
    20  	"database/sql"
    21  	"database/sql/driver"
    22  	"net"
    23  )
    24  
    25  // MySQLDriver is exported to make the driver directly accessible.
    26  // In general the driver is used via the database/sql package.
    27  type MySQLDriver struct{}
    28  
    29  // DialFunc is a function which can be used to establish the network connection.
    30  // Custom dial functions must be registered with RegisterDial
    31  type DialFunc func(addr string) (net.Conn, error)
    32  
    33  var dials map[string]DialFunc
    34  
    35  // RegisterDial registers a custom dial function. It can then be used by the
    36  // network address mynet(addr), where mynet is the registered new network.
    37  // addr is passed as a parameter to the dial function.
    38  func RegisterDial(net string, dial DialFunc) {
    39  	if dials == nil {
    40  		dials = make(map[string]DialFunc)
    41  	}
    42  	dials[net] = dial
    43  }
    44  
    45  // Open new Connection.
    46  // See https://yougam/libraries/go-sql-driver/mysql#dsn-data-source-name for how
    47  // the DSN string is formated
    48  func (d MySQLDriver) Open(dsn string) (driver.Conn, error) {
    49  	var err error
    50  
    51  	// New mysqlConn
    52  	mc := &mysqlConn{
    53  		maxPacketAllowed: maxPacketSize,
    54  		maxWriteSize:     maxPacketSize - 1,
    55  	}
    56  	mc.cfg, err = ParseDSN(dsn)
    57  	if err != nil {
    58  		return nil, err
    59  	}
    60  	mc.parseTime = mc.cfg.ParseTime
    61  	mc.strict = mc.cfg.Strict
    62  
    63  	// Connect to Server
    64  	if dial, ok := dials[mc.cfg.Net]; ok {
    65  		mc.netConn, err = dial(mc.cfg.Addr)
    66  	} else {
    67  		nd := net.Dialer{Timeout: mc.cfg.Timeout}
    68  		mc.netConn, err = nd.Dial(mc.cfg.Net, mc.cfg.Addr)
    69  	}
    70  	if err != nil {
    71  		return nil, err
    72  	}
    73  
    74  	// Enable TCP Keepalives on TCP connections
    75  	if tc, ok := mc.netConn.(*net.TCPConn); ok {
    76  		if err := tc.SetKeepAlive(true); err != nil {
    77  			// Don't send COM_QUIT before handshake.
    78  			mc.netConn.Close()
    79  			mc.netConn = nil
    80  			return nil, err
    81  		}
    82  	}
    83  
    84  	mc.buf = newBuffer(mc.netConn)
    85  
    86  	// Set I/O timeouts
    87  	mc.buf.timeout = mc.cfg.ReadTimeout
    88  	mc.writeTimeout = mc.cfg.WriteTimeout
    89  
    90  	// Reading Handshake Initialization Packet
    91  	cipher, err := mc.readInitPacket()
    92  	if err != nil {
    93  		mc.cleanup()
    94  		return nil, err
    95  	}
    96  
    97  	// Send Client Authentication Packet
    98  	if err = mc.writeAuthPacket(cipher); err != nil {
    99  		mc.cleanup()
   100  		return nil, err
   101  	}
   102  
   103  	// Handle response to auth packet, switch methods if possible
   104  	if err = handleAuthResult(mc, cipher); err != nil {
   105  		// Authentication failed and MySQL has already closed the connection
   106  		// (https://dev.mysql.com/doc/internals/en/authentication-fails.html).
   107  		// Do not send COM_QUIT, just cleanup and return the error.
   108  		mc.cleanup()
   109  		return nil, err
   110  	}
   111  
   112  	// Get max allowed packet size
   113  	maxap, err := mc.getSystemVar("max_allowed_packet")
   114  	if err != nil {
   115  		mc.Close()
   116  		return nil, err
   117  	}
   118  	mc.maxPacketAllowed = stringToInt(maxap) - 1
   119  	if mc.maxPacketAllowed < maxPacketSize {
   120  		mc.maxWriteSize = mc.maxPacketAllowed
   121  	}
   122  
   123  	// Handle DSN Params
   124  	err = mc.handleParams()
   125  	if err != nil {
   126  		mc.Close()
   127  		return nil, err
   128  	}
   129  
   130  	return mc, nil
   131  }
   132  
   133  func handleAuthResult(mc *mysqlConn, cipher []byte) error {
   134  	// Read Result Packet
   135  	err := mc.readResultOK()
   136  	if err == nil {
   137  		return nil // auth successful
   138  	}
   139  
   140  	if mc.cfg == nil {
   141  		return err // auth failed and retry not possible
   142  	}
   143  
   144  	// Retry auth if configured to do so.
   145  	if mc.cfg.AllowOldPasswords && err == ErrOldPassword {
   146  		// Retry with old authentication method. Note: there are edge cases
   147  		// where this should work but doesn't; this is currently "wontfix":
   148  		// https://yougam/libraries/go-sql-driver/mysql/issues/184
   149  		if err = mc.writeOldAuthPacket(cipher); err != nil {
   150  			return err
   151  		}
   152  		err = mc.readResultOK()
   153  	} else if mc.cfg.AllowCleartextPasswords && err == ErrCleartextPassword {
   154  		// Retry with clear text password for
   155  		// http://dev.mysql.com/doc/refman/5.7/en/cleartext-authentication-plugin.html
   156  		// http://dev.mysql.com/doc/refman/5.7/en/pam-authentication-plugin.html
   157  		if err = mc.writeClearAuthPacket(); err != nil {
   158  			return err
   159  		}
   160  		err = mc.readResultOK()
   161  	}
   162  	return err
   163  }
   164  
   165  func init() {
   166  	sql.Register("mysql", &MySQLDriver{})
   167  }