utils.go 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. package mysql
  2. import (
  3. "bufio"
  4. "bytes"
  5. "errors"
  6. "fmt"
  7. "io"
  8. "os"
  9. "strings"
  10. "time"
  11. "unicode"
  12. )
  13. // Version returns mymysql version string
  14. func Version() string {
  15. return "1.5.3"
  16. }
  17. func syntaxError(ln int) error {
  18. return fmt.Errorf("syntax error at line: %d", ln)
  19. }
  20. // Creates new conneection handler using configuration in cfgFile. Returns
  21. // connection handler and map contains unknown options.
  22. //
  23. // Config file format(example):
  24. //
  25. // # mymysql options (if some option isn't specified it defaults to "")
  26. //
  27. // DbRaddr 127.0.0.1:3306
  28. // # DbRaddr /var/run/mysqld/mysqld.sock
  29. // DbUser testuser
  30. // DbPass TestPasswd9
  31. // # optional: DbName test
  32. // # optional: DbEncd utf8
  33. // # optional: DbLaddr 127.0.0.1:0
  34. // # optional: DbTimeout 15s
  35. //
  36. // # Your options (returned in unk)
  37. //
  38. // MyOpt some text
  39. func NewFromCF(cfgFile string) (con Conn, unk map[string]string, err error) {
  40. var cf *os.File
  41. cf, err = os.Open(cfgFile)
  42. if err != nil {
  43. return
  44. }
  45. br := bufio.NewReader(cf)
  46. um := make(map[string]string)
  47. var proto, laddr, raddr, user, pass, name, encd, to string
  48. for i := 1; ; i++ {
  49. buf, isPrefix, e := br.ReadLine()
  50. if e != nil {
  51. if e == io.EOF {
  52. break
  53. }
  54. err = e
  55. return
  56. }
  57. l := string(buf)
  58. if isPrefix {
  59. err = fmt.Errorf("line %d is too long", i)
  60. return
  61. }
  62. l = strings.TrimFunc(l, unicode.IsSpace)
  63. if len(l) == 0 || l[0] == '#' {
  64. continue
  65. }
  66. n := strings.IndexFunc(l, unicode.IsSpace)
  67. if n == -1 {
  68. err = fmt.Errorf("syntax error at line: %d", i)
  69. return
  70. }
  71. v := l[:n]
  72. l = strings.TrimLeftFunc(l[n:], unicode.IsSpace)
  73. switch v {
  74. case "DbLaddr":
  75. laddr = l
  76. case "DbRaddr":
  77. raddr = l
  78. proto = "tcp"
  79. if strings.IndexRune(l, ':') == -1 {
  80. proto = "unix"
  81. }
  82. case "DbUser":
  83. user = l
  84. case "DbPass":
  85. pass = l
  86. case "DbName":
  87. name = l
  88. case "DbEncd":
  89. encd = l
  90. case "DbTimeout":
  91. to = l
  92. default:
  93. um[v] = l
  94. }
  95. }
  96. if raddr == "" {
  97. err = errors.New("DbRaddr option is empty")
  98. return
  99. }
  100. unk = um
  101. if name != "" {
  102. con = New(proto, laddr, raddr, user, pass, name)
  103. } else {
  104. con = New(proto, laddr, raddr, user, pass)
  105. }
  106. if encd != "" {
  107. con.Register(fmt.Sprintf("SET NAMES %s", encd))
  108. }
  109. if to != "" {
  110. var timeout time.Duration
  111. timeout, err = time.ParseDuration(to)
  112. if err != nil {
  113. return
  114. }
  115. con.SetTimeout(timeout)
  116. }
  117. return
  118. }
  119. // Calls Start and next calls GetRow as long as it reads all rows from the
  120. // result. Next it returns all readed rows as the slice of rows.
  121. func Query(c Conn, sql string, params ...interface{}) (rows []Row, res Result, err error) {
  122. res, err = c.Start(sql, params...)
  123. if err != nil {
  124. return
  125. }
  126. rows, err = GetRows(res)
  127. return
  128. }
  129. // Calls Start and next calls GetFirstRow
  130. func QueryFirst(c Conn, sql string, params ...interface{}) (row Row, res Result, err error) {
  131. res, err = c.Start(sql, params...)
  132. if err != nil {
  133. return
  134. }
  135. row, err = GetFirstRow(res)
  136. return
  137. }
  138. // Calls Start and next calls GetLastRow
  139. func QueryLast(c Conn, sql string, params ...interface{}) (row Row, res Result, err error) {
  140. res, err = c.Start(sql, params...)
  141. if err != nil {
  142. return
  143. }
  144. row, err = GetLastRow(res)
  145. return
  146. }
  147. // Calls Run and next call GetRow as long as it reads all rows from the
  148. // result. Next it returns all readed rows as the slice of rows.
  149. func Exec(s Stmt, params ...interface{}) (rows []Row, res Result, err error) {
  150. res, err = s.Run(params...)
  151. if err != nil {
  152. return
  153. }
  154. rows, err = GetRows(res)
  155. return
  156. }
  157. // Calls Run and next call GetFirstRow
  158. func ExecFirst(s Stmt, params ...interface{}) (row Row, res Result, err error) {
  159. res, err = s.Run(params...)
  160. if err != nil {
  161. return
  162. }
  163. row, err = GetFirstRow(res)
  164. return
  165. }
  166. // Calls Run and next call GetLastRow
  167. func ExecLast(s Stmt, params ...interface{}) (row Row, res Result, err error) {
  168. res, err = s.Run(params...)
  169. if err != nil {
  170. return
  171. }
  172. row, err = GetLastRow(res)
  173. return
  174. }
  175. // Calls r.MakeRow and next r.ScanRow. Doesn't return io.EOF error (returns nil
  176. // row insted).
  177. func GetRow(r Result) (Row, error) {
  178. row := r.MakeRow()
  179. err := r.ScanRow(row)
  180. if err != nil {
  181. if err == io.EOF {
  182. return nil, nil
  183. }
  184. return nil, err
  185. }
  186. return row, nil
  187. }
  188. // Reads all rows from result and returns them as slice.
  189. func GetRows(r Result) (rows []Row, err error) {
  190. var row Row
  191. for {
  192. row, err = r.GetRow()
  193. if err != nil || row == nil {
  194. break
  195. }
  196. rows = append(rows, row)
  197. }
  198. return
  199. }
  200. // Returns last row and discard others
  201. func GetLastRow(r Result) (Row, error) {
  202. row := r.MakeRow()
  203. err := r.ScanRow(row)
  204. if err == io.EOF {
  205. return nil, nil
  206. }
  207. for err == nil {
  208. err = r.ScanRow(row)
  209. }
  210. if err == io.EOF {
  211. return row, nil
  212. }
  213. return nil, err
  214. }
  215. // Read all unreaded rows and discard them. This function is useful if you
  216. // don't want to use the remaining rows. It has an impact only on current
  217. // result. If there is multi result query, you must use NextResult method and
  218. // read/discard all rows in this result, before use other method that sends
  219. // data to the server. You can't use this function if last GetRow returned nil.
  220. func End(r Result) error {
  221. _, err := GetLastRow(r)
  222. return err
  223. }
  224. // Returns first row and discard others
  225. func GetFirstRow(r Result) (row Row, err error) {
  226. row, err = r.GetRow()
  227. if err == nil && row != nil {
  228. err = r.End()
  229. }
  230. return
  231. }
  232. func escapeString(txt string) string {
  233. var (
  234. esc string
  235. buf bytes.Buffer
  236. )
  237. last := 0
  238. for ii, bb := range txt {
  239. switch bb {
  240. case 0:
  241. esc = `\0`
  242. case '\n':
  243. esc = `\n`
  244. case '\r':
  245. esc = `\r`
  246. case '\\':
  247. esc = `\\`
  248. case '\'':
  249. esc = `\'`
  250. case '"':
  251. esc = `\"`
  252. case '\032':
  253. esc = `\Z`
  254. default:
  255. continue
  256. }
  257. io.WriteString(&buf, txt[last:ii])
  258. io.WriteString(&buf, esc)
  259. last = ii + 1
  260. }
  261. io.WriteString(&buf, txt[last:])
  262. return buf.String()
  263. }
  264. func escapeQuotes(txt string) string {
  265. var buf bytes.Buffer
  266. last := 0
  267. for ii, bb := range txt {
  268. if bb == '\'' {
  269. io.WriteString(&buf, txt[last:ii])
  270. io.WriteString(&buf, `''`)
  271. last = ii + 1
  272. }
  273. }
  274. io.WriteString(&buf, txt[last:])
  275. return buf.String()
  276. }
  277. // Escapes special characters in the txt, so it is safe to place returned string
  278. // to Query method.
  279. func Escape(c Conn, txt string) string {
  280. if c.Status()&SERVER_STATUS_NO_BACKSLASH_ESCAPES != 0 {
  281. return escapeQuotes(txt)
  282. }
  283. return escapeString(txt)
  284. }