123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302 |
- 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)
- }
|