github.com/cellofellow/gopkg@v0.0.0-20140722061823-eec0544a62ad/database/mysql/connection.go (about)

     1  // Go MySQL Driver - A MySQL-Driver for Go's database/sql package
     2  //
     3  // Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved.
     4  //
     5  // This Source Code Form is subject to the terms of the Mozilla Public
     6  // License, v. 2.0. If a copy of the MPL was not distributed with this file,
     7  // You can obtain one at http://mozilla.org/MPL/2.0/.
     8  
     9  package mysql
    10  
    11  import (
    12  	"crypto/tls"
    13  	"database/sql/driver"
    14  	"errors"
    15  	"net"
    16  	"strings"
    17  	"time"
    18  )
    19  
    20  type mysqlConn struct {
    21  	buf              buffer
    22  	netConn          net.Conn
    23  	affectedRows     uint64
    24  	insertId         uint64
    25  	cfg              *config
    26  	maxPacketAllowed int
    27  	maxWriteSize     int
    28  	flags            clientFlag
    29  	sequence         uint8
    30  	parseTime        bool
    31  	strict           bool
    32  }
    33  
    34  type config struct {
    35  	user              string
    36  	passwd            string
    37  	net               string
    38  	addr              string
    39  	dbname            string
    40  	params            map[string]string
    41  	loc               *time.Location
    42  	tls               *tls.Config
    43  	timeout           time.Duration
    44  	collation         uint8
    45  	allowAllFiles     bool
    46  	allowOldPasswords bool
    47  	clientFoundRows   bool
    48  }
    49  
    50  // Handles parameters set in DSN after the connection is established
    51  func (mc *mysqlConn) handleParams() (err error) {
    52  	for param, val := range mc.cfg.params {
    53  		switch param {
    54  		// Charset
    55  		case "charset":
    56  			charsets := strings.Split(val, ",")
    57  			for i := range charsets {
    58  				// ignore errors here - a charset may not exist
    59  				err = mc.exec("SET NAMES " + charsets[i])
    60  				if err == nil {
    61  					break
    62  				}
    63  			}
    64  			if err != nil {
    65  				return
    66  			}
    67  
    68  		// time.Time parsing
    69  		case "parseTime":
    70  			var isBool bool
    71  			mc.parseTime, isBool = readBool(val)
    72  			if !isBool {
    73  				return errors.New("Invalid Bool value: " + val)
    74  			}
    75  
    76  		// Strict mode
    77  		case "strict":
    78  			var isBool bool
    79  			mc.strict, isBool = readBool(val)
    80  			if !isBool {
    81  				return errors.New("Invalid Bool value: " + val)
    82  			}
    83  
    84  		// Compression
    85  		case "compress":
    86  			err = errors.New("Compression not implemented yet")
    87  			return
    88  
    89  		// System Vars
    90  		default:
    91  			err = mc.exec("SET " + param + "=" + val + "")
    92  			if err != nil {
    93  				return
    94  			}
    95  		}
    96  	}
    97  
    98  	return
    99  }
   100  
   101  func (mc *mysqlConn) Begin() (driver.Tx, error) {
   102  	if mc.netConn == nil {
   103  		errLog.Print(ErrInvalidConn)
   104  		return nil, driver.ErrBadConn
   105  	}
   106  	err := mc.exec("START TRANSACTION")
   107  	if err == nil {
   108  		return &mysqlTx{mc}, err
   109  	}
   110  
   111  	return nil, err
   112  }
   113  
   114  func (mc *mysqlConn) Close() (err error) {
   115  	// Makes Close idempotent
   116  	if mc.netConn != nil {
   117  		err = mc.writeCommandPacket(comQuit)
   118  		if err == nil {
   119  			err = mc.netConn.Close()
   120  		} else {
   121  			mc.netConn.Close()
   122  		}
   123  		mc.netConn = nil
   124  	}
   125  
   126  	mc.cfg = nil
   127  	mc.buf.rd = nil
   128  
   129  	return
   130  }
   131  
   132  func (mc *mysqlConn) Prepare(query string) (driver.Stmt, error) {
   133  	if mc.netConn == nil {
   134  		errLog.Print(ErrInvalidConn)
   135  		return nil, driver.ErrBadConn
   136  	}
   137  	// Send command
   138  	err := mc.writeCommandPacketStr(comStmtPrepare, query)
   139  	if err != nil {
   140  		return nil, err
   141  	}
   142  
   143  	stmt := &mysqlStmt{
   144  		mc: mc,
   145  	}
   146  
   147  	// Read Result
   148  	columnCount, err := stmt.readPrepareResultPacket()
   149  	if err == nil {
   150  		if stmt.paramCount > 0 {
   151  			if err = mc.readUntilEOF(); err != nil {
   152  				return nil, err
   153  			}
   154  		}
   155  
   156  		if columnCount > 0 {
   157  			err = mc.readUntilEOF()
   158  		}
   159  	}
   160  
   161  	return stmt, err
   162  }
   163  
   164  func (mc *mysqlConn) Exec(query string, args []driver.Value) (driver.Result, error) {
   165  	if mc.netConn == nil {
   166  		errLog.Print(ErrInvalidConn)
   167  		return nil, driver.ErrBadConn
   168  	}
   169  	if len(args) == 0 { // no args, fastpath
   170  		mc.affectedRows = 0
   171  		mc.insertId = 0
   172  
   173  		err := mc.exec(query)
   174  		if err == nil {
   175  			return &mysqlResult{
   176  				affectedRows: int64(mc.affectedRows),
   177  				insertId:     int64(mc.insertId),
   178  			}, err
   179  		}
   180  		return nil, err
   181  	}
   182  
   183  	// with args, must use prepared stmt
   184  	return nil, driver.ErrSkip
   185  
   186  }
   187  
   188  // Internal function to execute commands
   189  func (mc *mysqlConn) exec(query string) error {
   190  	// Send command
   191  	err := mc.writeCommandPacketStr(comQuery, query)
   192  	if err != nil {
   193  		return err
   194  	}
   195  
   196  	// Read Result
   197  	resLen, err := mc.readResultSetHeaderPacket()
   198  	if err == nil && resLen > 0 {
   199  		if err = mc.readUntilEOF(); err != nil {
   200  			return err
   201  		}
   202  
   203  		err = mc.readUntilEOF()
   204  	}
   205  
   206  	return err
   207  }
   208  
   209  func (mc *mysqlConn) Query(query string, args []driver.Value) (driver.Rows, error) {
   210  	if mc.netConn == nil {
   211  		errLog.Print(ErrInvalidConn)
   212  		return nil, driver.ErrBadConn
   213  	}
   214  	if len(args) == 0 { // no args, fastpath
   215  		// Send command
   216  		err := mc.writeCommandPacketStr(comQuery, query)
   217  		if err == nil {
   218  			// Read Result
   219  			var resLen int
   220  			resLen, err = mc.readResultSetHeaderPacket()
   221  			if err == nil {
   222  				rows := new(textRows)
   223  				rows.mc = mc
   224  
   225  				if resLen > 0 {
   226  					// Columns
   227  					rows.columns, err = mc.readColumns(resLen)
   228  				}
   229  				return rows, err
   230  			}
   231  		}
   232  		return nil, err
   233  	}
   234  
   235  	// with args, must use prepared stmt
   236  	return nil, driver.ErrSkip
   237  }
   238  
   239  // Gets the value of the given MySQL System Variable
   240  // The returned byte slice is only valid until the next read
   241  func (mc *mysqlConn) getSystemVar(name string) ([]byte, error) {
   242  	// Send command
   243  	if err := mc.writeCommandPacketStr(comQuery, "SELECT @@"+name); err != nil {
   244  		return nil, err
   245  	}
   246  
   247  	// Read Result
   248  	resLen, err := mc.readResultSetHeaderPacket()
   249  	if err == nil {
   250  		rows := new(textRows)
   251  		rows.mc = mc
   252  
   253  		if resLen > 0 {
   254  			// Columns
   255  			if err := mc.readUntilEOF(); err != nil {
   256  				return nil, err
   257  			}
   258  		}
   259  
   260  		dest := make([]driver.Value, resLen)
   261  		if err = rows.readRow(dest); err == nil {
   262  			return dest[0].([]byte), mc.readUntilEOF()
   263  		}
   264  	}
   265  	return nil, err
   266  }