clair/vendor/github.com/ziutek/mymysql/mysql/utils.go
2016-02-24 16:34:54 -05:00

303 lines
6.2 KiB
Go

package mysql
import (
"bufio"
"bytes"
"errors"
"fmt"
"io"
"os"
"strings"
"time"
"unicode"
)
// Version returns mymysql version string
func Version() string {
return "1.5.3"
}
func syntaxError(ln int) error {
return fmt.Errorf("syntax error at line: %d", ln)
}
// Creates new conneection handler using configuration in cfgFile. Returns
// connection handler and map contains unknown options.
//
// Config file format(example):
//
// # mymysql options (if some option isn't specified it defaults to "")
//
// DbRaddr 127.0.0.1:3306
// # DbRaddr /var/run/mysqld/mysqld.sock
// DbUser testuser
// DbPass TestPasswd9
// # optional: DbName test
// # optional: DbEncd utf8
// # optional: DbLaddr 127.0.0.1:0
// # optional: DbTimeout 15s
//
// # Your options (returned in unk)
//
// MyOpt some text
func NewFromCF(cfgFile string) (con Conn, unk map[string]string, err error) {
var cf *os.File
cf, err = os.Open(cfgFile)
if err != nil {
return
}
br := bufio.NewReader(cf)
um := make(map[string]string)
var proto, laddr, raddr, user, pass, name, encd, to string
for i := 1; ; i++ {
buf, isPrefix, e := br.ReadLine()
if e != nil {
if e == io.EOF {
break
}
err = e
return
}
l := string(buf)
if isPrefix {
err = fmt.Errorf("line %d is too long", i)
return
}
l = strings.TrimFunc(l, unicode.IsSpace)
if len(l) == 0 || l[0] == '#' {
continue
}
n := strings.IndexFunc(l, unicode.IsSpace)
if n == -1 {
err = fmt.Errorf("syntax error at line: %d", i)
return
}
v := l[:n]
l = strings.TrimLeftFunc(l[n:], unicode.IsSpace)
switch v {
case "DbLaddr":
laddr = l
case "DbRaddr":
raddr = l
proto = "tcp"
if strings.IndexRune(l, ':') == -1 {
proto = "unix"
}
case "DbUser":
user = l
case "DbPass":
pass = l
case "DbName":
name = l
case "DbEncd":
encd = l
case "DbTimeout":
to = l
default:
um[v] = l
}
}
if raddr == "" {
err = errors.New("DbRaddr option is empty")
return
}
unk = um
if name != "" {
con = New(proto, laddr, raddr, user, pass, name)
} else {
con = New(proto, laddr, raddr, user, pass)
}
if encd != "" {
con.Register(fmt.Sprintf("SET NAMES %s", encd))
}
if to != "" {
var timeout time.Duration
timeout, err = time.ParseDuration(to)
if err != nil {
return
}
con.SetTimeout(timeout)
}
return
}
// Calls Start and next calls GetRow as long as it reads all rows from the
// result. Next it returns all readed rows as the slice of rows.
func Query(c Conn, sql string, params ...interface{}) (rows []Row, res Result, err error) {
res, err = c.Start(sql, params...)
if err != nil {
return
}
rows, err = GetRows(res)
return
}
// Calls Start and next calls GetFirstRow
func QueryFirst(c Conn, sql string, params ...interface{}) (row Row, res Result, err error) {
res, err = c.Start(sql, params...)
if err != nil {
return
}
row, err = GetFirstRow(res)
return
}
// Calls Start and next calls GetLastRow
func QueryLast(c Conn, sql string, params ...interface{}) (row Row, res Result, err error) {
res, err = c.Start(sql, params...)
if err != nil {
return
}
row, err = GetLastRow(res)
return
}
// Calls Run and next call GetRow as long as it reads all rows from the
// result. Next it returns all readed rows as the slice of rows.
func Exec(s Stmt, params ...interface{}) (rows []Row, res Result, err error) {
res, err = s.Run(params...)
if err != nil {
return
}
rows, err = GetRows(res)
return
}
// Calls Run and next call GetFirstRow
func ExecFirst(s Stmt, params ...interface{}) (row Row, res Result, err error) {
res, err = s.Run(params...)
if err != nil {
return
}
row, err = GetFirstRow(res)
return
}
// Calls Run and next call GetLastRow
func ExecLast(s Stmt, params ...interface{}) (row Row, res Result, err error) {
res, err = s.Run(params...)
if err != nil {
return
}
row, err = GetLastRow(res)
return
}
// Calls r.MakeRow and next r.ScanRow. Doesn't return io.EOF error (returns nil
// row insted).
func GetRow(r Result) (Row, error) {
row := r.MakeRow()
err := r.ScanRow(row)
if err != nil {
if err == io.EOF {
return nil, nil
}
return nil, err
}
return row, nil
}
// Reads all rows from result and returns them as slice.
func GetRows(r Result) (rows []Row, err error) {
var row Row
for {
row, err = r.GetRow()
if err != nil || row == nil {
break
}
rows = append(rows, row)
}
return
}
// Returns last row and discard others
func GetLastRow(r Result) (Row, error) {
row := r.MakeRow()
err := r.ScanRow(row)
if err == io.EOF {
return nil, nil
}
for err == nil {
err = r.ScanRow(row)
}
if err == io.EOF {
return row, nil
}
return nil, err
}
// Read all unreaded rows and discard them. This function is useful if you
// don't want to use the remaining rows. It has an impact only on current
// result. If there is multi result query, you must use NextResult method and
// read/discard all rows in this result, before use other method that sends
// data to the server. You can't use this function if last GetRow returned nil.
func End(r Result) error {
_, err := GetLastRow(r)
return err
}
// Returns first row and discard others
func GetFirstRow(r Result) (row Row, err error) {
row, err = r.GetRow()
if err == nil && row != nil {
err = r.End()
}
return
}
func escapeString(txt string) string {
var (
esc string
buf bytes.Buffer
)
last := 0
for ii, bb := range txt {
switch bb {
case 0:
esc = `\0`
case '\n':
esc = `\n`
case '\r':
esc = `\r`
case '\\':
esc = `\\`
case '\'':
esc = `\'`
case '"':
esc = `\"`
case '\032':
esc = `\Z`
default:
continue
}
io.WriteString(&buf, txt[last:ii])
io.WriteString(&buf, esc)
last = ii + 1
}
io.WriteString(&buf, txt[last:])
return buf.String()
}
func escapeQuotes(txt string) string {
var buf bytes.Buffer
last := 0
for ii, bb := range txt {
if bb == '\'' {
io.WriteString(&buf, txt[last:ii])
io.WriteString(&buf, `''`)
last = ii + 1
}
}
io.WriteString(&buf, txt[last:])
return buf.String()
}
// Escapes special characters in the txt, so it is safe to place returned string
// to Query method.
func Escape(c Conn, txt string) string {
if c.Status()&SERVER_STATUS_NO_BACKSLASH_ESCAPES != 0 {
return escapeQuotes(txt)
}
return escapeString(txt)
}