Skip to content

Commit b8ae1f2

Browse files
committed
Milestone 1
0 parents  commit b8ae1f2

10 files changed

+2470
-0
lines changed

LICENSE

+373
Large diffs are not rendered by default.

connection.go

+285
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,285 @@
1+
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
2+
//
3+
// Copyright 2012 Julien Schmidt. All rights reserved.
4+
// http://www.julienschmidt.com
5+
//
6+
// This Source Code Form is subject to the terms of the Mozilla Public
7+
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
8+
// You can obtain one at http://mozilla.org/MPL/2.0/.
9+
package mysql
10+
11+
import (
12+
"database/sql/driver"
13+
"errors"
14+
"net"
15+
"strconv"
16+
"time"
17+
)
18+
19+
type mysqlConn struct {
20+
cfg *config
21+
server *serverSettings
22+
netConn net.Conn
23+
protocol uint8
24+
sequence uint8
25+
affectedRows uint64
26+
insertId uint64
27+
lastCmdTime time.Time
28+
keepaliveTimer *time.Timer
29+
}
30+
31+
type config struct {
32+
user string
33+
passwd string
34+
net string
35+
addr string
36+
dbname string
37+
params map[string]string
38+
}
39+
40+
type serverSettings struct {
41+
protocol byte
42+
version string
43+
flags ClientFlag
44+
charset uint8
45+
scrambleBuff []byte
46+
threadID uint32
47+
keepalive int64
48+
}
49+
50+
// Handles parameters set in DSN
51+
func (mc *mysqlConn) handleParams() (e error) {
52+
for param, val := range mc.cfg.params {
53+
switch param {
54+
// Charset
55+
case "charset":
56+
e = mc.exec("SET NAMES " + val)
57+
if e != nil {
58+
return
59+
}
60+
61+
// TLS-Encryption
62+
case "tls":
63+
debug("TLS-Encryption not implemented yet")
64+
65+
// Compression
66+
case "compress":
67+
debug("TLS-Encryption not implemented yet")
68+
69+
// We don't want to set keepalive as system var
70+
case "keepalive":
71+
break
72+
73+
// System Vars
74+
default:
75+
e = mc.exec("SET " + param + "=" + val + "")
76+
if e != nil {
77+
return
78+
}
79+
}
80+
}
81+
82+
// KeepAlive
83+
if val, param := mc.cfg.params["keepalive"]; param {
84+
mc.server.keepalive, e = strconv.ParseInt(val, 10, 64)
85+
if e != nil {
86+
return errors.New("Invalid keepalive time")
87+
}
88+
89+
// Get keepalive time by MySQL system var wait_timeout
90+
if mc.server.keepalive == 1 {
91+
val, e = mc.getSystemVar("wait_timeout")
92+
mc.server.keepalive, e = strconv.ParseInt(val, 10, 64)
93+
if e != nil {
94+
return errors.New("Error getting wait_timeout")
95+
}
96+
97+
// Trigger 1min BEFORE wait_timeout
98+
if mc.server.keepalive > 60 {
99+
mc.server.keepalive -= 60
100+
}
101+
}
102+
103+
if mc.server.keepalive > 0 {
104+
mc.lastCmdTime = time.Now()
105+
106+
// Ping-Timer to avoid timeout
107+
mc.keepaliveTimer = time.AfterFunc(
108+
time.Duration(mc.server.keepalive)*time.Second, func() {
109+
var diff time.Duration
110+
for {
111+
// Fires only if diff > keepalive. Makes it collision safe
112+
for mc.netConn != nil &&
113+
mc.lastCmdTime.Unix()+mc.server.keepalive > time.Now().Unix() {
114+
diff = mc.lastCmdTime.Sub(time.Unix(time.Now().Unix()-mc.server.keepalive, 0))
115+
time.Sleep(diff)
116+
}
117+
if mc.netConn != nil {
118+
if e := mc.Ping(); e != nil {
119+
break
120+
}
121+
} else {
122+
return
123+
}
124+
}
125+
})
126+
}
127+
}
128+
return
129+
}
130+
131+
func (mc *mysqlConn) Begin() (driver.Tx, error) {
132+
e := mc.exec("START TRANSACTION")
133+
if e != nil {
134+
return nil, e
135+
}
136+
137+
return &mysqlTx{mc}, e
138+
}
139+
140+
func (mc *mysqlConn) Close() (e error) {
141+
if mc.server.keepalive > 0 {
142+
mc.keepaliveTimer.Stop()
143+
}
144+
mc.writeCommandPacket(COM_QUIT)
145+
mc.netConn = nil
146+
return
147+
}
148+
149+
func (mc *mysqlConn) Prepare(query string) (ds driver.Stmt, e error) {
150+
// Send command
151+
e = mc.writeCommandPacket(COM_STMT_PREPARE, query)
152+
if e != nil {
153+
return
154+
}
155+
156+
stmt := mysqlStmt{new(stmtContent)}
157+
stmt.mc = mc
158+
159+
// Read Result
160+
var columnCount, paramCount uint16
161+
stmt.id, columnCount, paramCount, e = mc.readPrepareResultPacket()
162+
if e != nil {
163+
return
164+
}
165+
166+
if paramCount > 0 {
167+
stmt.params, e = stmt.mc.readColumns(int(paramCount))
168+
if e != nil {
169+
return
170+
}
171+
}
172+
stmt.paramCount = int(paramCount)
173+
174+
if columnCount > 0 {
175+
_, e = stmt.mc.readColumns(int(columnCount))
176+
if e != nil {
177+
return
178+
}
179+
}
180+
181+
stmt.query = query
182+
ds = stmt
183+
return
184+
}
185+
186+
func (mc *mysqlConn) Exec(query string, args []driver.Value) (driver.Result, error) {
187+
if len(args) > 0 {
188+
return nil, driver.ErrSkip
189+
}
190+
191+
mc.affectedRows = 0
192+
mc.insertId = 0
193+
194+
e := mc.exec(query)
195+
if e != nil {
196+
return nil, e
197+
}
198+
199+
if mc.affectedRows == 0 {
200+
return driver.ResultNoRows, e
201+
}
202+
203+
return &mysqlResult{
204+
affectedRows: int64(mc.affectedRows),
205+
insertId: int64(mc.insertId)},
206+
e
207+
}
208+
209+
// Internal function to execute statements
210+
func (mc *mysqlConn) exec(query string) (e error) {
211+
// Send command
212+
e = mc.writeCommandPacket(COM_QUERY, query)
213+
if e != nil {
214+
return
215+
}
216+
217+
// Read Result
218+
resLen, e := mc.readResultSetHeaderPacket()
219+
if e != nil {
220+
return
221+
}
222+
223+
mc.affectedRows = 0
224+
mc.insertId = 0
225+
226+
if resLen > 0 {
227+
_, e = mc.readUntilEOF()
228+
if e != nil {
229+
return
230+
}
231+
232+
mc.affectedRows, e = mc.readUntilEOF()
233+
if e != nil {
234+
return
235+
}
236+
}
237+
238+
return
239+
}
240+
241+
// Gets the value of the given MySQL System Variable
242+
func (mc *mysqlConn) getSystemVar(name string) (val string, e error) {
243+
// Send command
244+
e = mc.writeCommandPacket(COM_QUERY, "SELECT @@"+name)
245+
if e != nil {
246+
return
247+
}
248+
249+
// Read Result
250+
resLen, e := mc.readResultSetHeaderPacket()
251+
if e != nil {
252+
return
253+
}
254+
255+
if resLen > 0 {
256+
var n uint64
257+
n, e = mc.readUntilEOF()
258+
if e != nil {
259+
return
260+
}
261+
262+
var rows []*[]*[]byte
263+
rows, e = mc.readRows(int(n))
264+
if e != nil {
265+
return
266+
}
267+
268+
val = string(*(*rows[0])[0])
269+
}
270+
271+
return
272+
}
273+
274+
// Executes a simple Ping-CMD to test or keepalive the connection
275+
func (mc *mysqlConn) Ping() (e error) {
276+
// Send command
277+
e = mc.writeCommandPacket(COM_PING)
278+
if e != nil {
279+
return
280+
}
281+
282+
// Read Result
283+
e = mc.readResultOK()
284+
return
285+
}

0 commit comments

Comments
 (0)