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 }