mirror of https://github.com/gogits/gogs.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
208 lines
4.1 KiB
208 lines
4.1 KiB
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package |
|
// |
|
// Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved. |
|
// |
|
// This Source Code Form is subject to the terms of the Mozilla Public |
|
// License, v. 2.0. If a copy of the MPL was not distributed with this file, |
|
// You can obtain one at http://mozilla.org/MPL/2.0/. |
|
|
|
// +build go1.8 |
|
|
|
package mysql |
|
|
|
import ( |
|
"context" |
|
"database/sql" |
|
"database/sql/driver" |
|
) |
|
|
|
// Ping implements driver.Pinger interface |
|
func (mc *mysqlConn) Ping(ctx context.Context) (err error) { |
|
if mc.closed.IsSet() { |
|
errLog.Print(ErrInvalidConn) |
|
return driver.ErrBadConn |
|
} |
|
|
|
if err = mc.watchCancel(ctx); err != nil { |
|
return |
|
} |
|
defer mc.finish() |
|
|
|
if err = mc.writeCommandPacket(comPing); err != nil { |
|
return |
|
} |
|
|
|
return mc.readResultOK() |
|
} |
|
|
|
// BeginTx implements driver.ConnBeginTx interface |
|
func (mc *mysqlConn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) { |
|
if err := mc.watchCancel(ctx); err != nil { |
|
return nil, err |
|
} |
|
defer mc.finish() |
|
|
|
if sql.IsolationLevel(opts.Isolation) != sql.LevelDefault { |
|
level, err := mapIsolationLevel(opts.Isolation) |
|
if err != nil { |
|
return nil, err |
|
} |
|
err = mc.exec("SET TRANSACTION ISOLATION LEVEL " + level) |
|
if err != nil { |
|
return nil, err |
|
} |
|
} |
|
|
|
return mc.begin(opts.ReadOnly) |
|
} |
|
|
|
func (mc *mysqlConn) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Rows, error) { |
|
dargs, err := namedValueToValue(args) |
|
if err != nil { |
|
return nil, err |
|
} |
|
|
|
if err := mc.watchCancel(ctx); err != nil { |
|
return nil, err |
|
} |
|
|
|
rows, err := mc.query(query, dargs) |
|
if err != nil { |
|
mc.finish() |
|
return nil, err |
|
} |
|
rows.finish = mc.finish |
|
return rows, err |
|
} |
|
|
|
func (mc *mysqlConn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Result, error) { |
|
dargs, err := namedValueToValue(args) |
|
if err != nil { |
|
return nil, err |
|
} |
|
|
|
if err := mc.watchCancel(ctx); err != nil { |
|
return nil, err |
|
} |
|
defer mc.finish() |
|
|
|
return mc.Exec(query, dargs) |
|
} |
|
|
|
func (mc *mysqlConn) PrepareContext(ctx context.Context, query string) (driver.Stmt, error) { |
|
if err := mc.watchCancel(ctx); err != nil { |
|
return nil, err |
|
} |
|
|
|
stmt, err := mc.Prepare(query) |
|
mc.finish() |
|
if err != nil { |
|
return nil, err |
|
} |
|
|
|
select { |
|
default: |
|
case <-ctx.Done(): |
|
stmt.Close() |
|
return nil, ctx.Err() |
|
} |
|
return stmt, nil |
|
} |
|
|
|
func (stmt *mysqlStmt) QueryContext(ctx context.Context, args []driver.NamedValue) (driver.Rows, error) { |
|
dargs, err := namedValueToValue(args) |
|
if err != nil { |
|
return nil, err |
|
} |
|
|
|
if err := stmt.mc.watchCancel(ctx); err != nil { |
|
return nil, err |
|
} |
|
|
|
rows, err := stmt.query(dargs) |
|
if err != nil { |
|
stmt.mc.finish() |
|
return nil, err |
|
} |
|
rows.finish = stmt.mc.finish |
|
return rows, err |
|
} |
|
|
|
func (stmt *mysqlStmt) ExecContext(ctx context.Context, args []driver.NamedValue) (driver.Result, error) { |
|
dargs, err := namedValueToValue(args) |
|
if err != nil { |
|
return nil, err |
|
} |
|
|
|
if err := stmt.mc.watchCancel(ctx); err != nil { |
|
return nil, err |
|
} |
|
defer stmt.mc.finish() |
|
|
|
return stmt.Exec(dargs) |
|
} |
|
|
|
func (mc *mysqlConn) watchCancel(ctx context.Context) error { |
|
if mc.watching { |
|
// Reach here if canceled, |
|
// so the connection is already invalid |
|
mc.cleanup() |
|
return nil |
|
} |
|
if ctx.Done() == nil { |
|
return nil |
|
} |
|
|
|
mc.watching = true |
|
select { |
|
default: |
|
case <-ctx.Done(): |
|
return ctx.Err() |
|
} |
|
if mc.watcher == nil { |
|
return nil |
|
} |
|
|
|
mc.watcher <- ctx |
|
|
|
return nil |
|
} |
|
|
|
func (mc *mysqlConn) startWatcher() { |
|
watcher := make(chan mysqlContext, 1) |
|
mc.watcher = watcher |
|
finished := make(chan struct{}) |
|
mc.finished = finished |
|
go func() { |
|
for { |
|
var ctx mysqlContext |
|
select { |
|
case ctx = <-watcher: |
|
case <-mc.closech: |
|
return |
|
} |
|
|
|
select { |
|
case <-ctx.Done(): |
|
mc.cancel(ctx.Err()) |
|
case <-finished: |
|
case <-mc.closech: |
|
return |
|
} |
|
} |
|
}() |
|
} |
|
|
|
func (mc *mysqlConn) CheckNamedValue(nv *driver.NamedValue) (err error) { |
|
nv.Value, err = converter{}.ConvertValue(nv.Value) |
|
return |
|
} |
|
|
|
// ResetSession implements driver.SessionResetter. |
|
// (From Go 1.10) |
|
func (mc *mysqlConn) ResetSession(ctx context.Context) error { |
|
if mc.closed.IsSet() { |
|
return driver.ErrBadConn |
|
} |
|
return nil |
|
}
|
|
|