stmt.go 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. package client
  2. import (
  3. "encoding/binary"
  4. "fmt"
  5. "math"
  6. "github.com/juju/errors"
  7. . "github.com/siddontang/go-mysql/mysql"
  8. )
  9. type Stmt struct {
  10. conn *Conn
  11. id uint32
  12. query string
  13. params int
  14. columns int
  15. }
  16. func (s *Stmt) ParamNum() int {
  17. return s.params
  18. }
  19. func (s *Stmt) ColumnNum() int {
  20. return s.columns
  21. }
  22. func (s *Stmt) Execute(args ...interface{}) (*Result, error) {
  23. if err := s.write(args...); err != nil {
  24. return nil, errors.Trace(err)
  25. }
  26. return s.conn.readResult(true)
  27. }
  28. func (s *Stmt) Close() error {
  29. if err := s.conn.writeCommandUint32(COM_STMT_CLOSE, s.id); err != nil {
  30. return errors.Trace(err)
  31. }
  32. return nil
  33. }
  34. func (s *Stmt) write(args ...interface{}) error {
  35. paramsNum := s.params
  36. if len(args) != paramsNum {
  37. return fmt.Errorf("argument mismatch, need %d but got %d", s.params, len(args))
  38. }
  39. paramTypes := make([]byte, paramsNum<<1)
  40. paramValues := make([][]byte, paramsNum)
  41. //NULL-bitmap, length: (num-params+7)
  42. nullBitmap := make([]byte, (paramsNum+7)>>3)
  43. var length int = int(1 + 4 + 1 + 4 + ((paramsNum + 7) >> 3) + 1 + (paramsNum << 1))
  44. var newParamBoundFlag byte = 0
  45. for i := range args {
  46. if args[i] == nil {
  47. nullBitmap[i/8] |= (1 << (uint(i) % 8))
  48. paramTypes[i<<1] = MYSQL_TYPE_NULL
  49. continue
  50. }
  51. newParamBoundFlag = 1
  52. switch v := args[i].(type) {
  53. case int8:
  54. paramTypes[i<<1] = MYSQL_TYPE_TINY
  55. paramValues[i] = []byte{byte(v)}
  56. case int16:
  57. paramTypes[i<<1] = MYSQL_TYPE_SHORT
  58. paramValues[i] = Uint16ToBytes(uint16(v))
  59. case int32:
  60. paramTypes[i<<1] = MYSQL_TYPE_LONG
  61. paramValues[i] = Uint32ToBytes(uint32(v))
  62. case int:
  63. paramTypes[i<<1] = MYSQL_TYPE_LONGLONG
  64. paramValues[i] = Uint64ToBytes(uint64(v))
  65. case int64:
  66. paramTypes[i<<1] = MYSQL_TYPE_LONGLONG
  67. paramValues[i] = Uint64ToBytes(uint64(v))
  68. case uint8:
  69. paramTypes[i<<1] = MYSQL_TYPE_TINY
  70. paramTypes[(i<<1)+1] = 0x80
  71. paramValues[i] = []byte{v}
  72. case uint16:
  73. paramTypes[i<<1] = MYSQL_TYPE_SHORT
  74. paramTypes[(i<<1)+1] = 0x80
  75. paramValues[i] = Uint16ToBytes(uint16(v))
  76. case uint32:
  77. paramTypes[i<<1] = MYSQL_TYPE_LONG
  78. paramTypes[(i<<1)+1] = 0x80
  79. paramValues[i] = Uint32ToBytes(uint32(v))
  80. case uint:
  81. paramTypes[i<<1] = MYSQL_TYPE_LONGLONG
  82. paramTypes[(i<<1)+1] = 0x80
  83. paramValues[i] = Uint64ToBytes(uint64(v))
  84. case uint64:
  85. paramTypes[i<<1] = MYSQL_TYPE_LONGLONG
  86. paramTypes[(i<<1)+1] = 0x80
  87. paramValues[i] = Uint64ToBytes(uint64(v))
  88. case bool:
  89. paramTypes[i<<1] = MYSQL_TYPE_TINY
  90. if v {
  91. paramValues[i] = []byte{1}
  92. } else {
  93. paramValues[i] = []byte{0}
  94. }
  95. case float32:
  96. paramTypes[i<<1] = MYSQL_TYPE_FLOAT
  97. paramValues[i] = Uint32ToBytes(math.Float32bits(v))
  98. case float64:
  99. paramTypes[i<<1] = MYSQL_TYPE_DOUBLE
  100. paramValues[i] = Uint64ToBytes(math.Float64bits(v))
  101. case string:
  102. paramTypes[i<<1] = MYSQL_TYPE_STRING
  103. paramValues[i] = append(PutLengthEncodedInt(uint64(len(v))), v...)
  104. case []byte:
  105. paramTypes[i<<1] = MYSQL_TYPE_STRING
  106. paramValues[i] = append(PutLengthEncodedInt(uint64(len(v))), v...)
  107. default:
  108. return fmt.Errorf("invalid argument type %T", args[i])
  109. }
  110. length += len(paramValues[i])
  111. }
  112. data := make([]byte, 4, 4+length)
  113. data = append(data, COM_STMT_EXECUTE)
  114. data = append(data, byte(s.id), byte(s.id>>8), byte(s.id>>16), byte(s.id>>24))
  115. //flag: CURSOR_TYPE_NO_CURSOR
  116. data = append(data, 0x00)
  117. //iteration-count, always 1
  118. data = append(data, 1, 0, 0, 0)
  119. if s.params > 0 {
  120. data = append(data, nullBitmap...)
  121. //new-params-bound-flag
  122. data = append(data, newParamBoundFlag)
  123. if newParamBoundFlag == 1 {
  124. //type of each parameter, length: num-params * 2
  125. data = append(data, paramTypes...)
  126. //value of each parameter
  127. for _, v := range paramValues {
  128. data = append(data, v...)
  129. }
  130. }
  131. }
  132. s.conn.ResetSequence()
  133. return s.conn.WritePacket(data)
  134. }
  135. func (c *Conn) Prepare(query string) (*Stmt, error) {
  136. if err := c.writeCommandStr(COM_STMT_PREPARE, query); err != nil {
  137. return nil, errors.Trace(err)
  138. }
  139. data, err := c.ReadPacket()
  140. if err != nil {
  141. return nil, errors.Trace(err)
  142. }
  143. if data[0] == ERR_HEADER {
  144. return nil, c.handleErrorPacket(data)
  145. } else if data[0] != OK_HEADER {
  146. return nil, ErrMalformPacket
  147. }
  148. s := new(Stmt)
  149. s.conn = c
  150. pos := 1
  151. //for statement id
  152. s.id = binary.LittleEndian.Uint32(data[pos:])
  153. pos += 4
  154. //number columns
  155. s.columns = int(binary.LittleEndian.Uint16(data[pos:]))
  156. pos += 2
  157. //number params
  158. s.params = int(binary.LittleEndian.Uint16(data[pos:]))
  159. pos += 2
  160. //warnings
  161. //warnings = binary.LittleEndian.Uint16(data[pos:])
  162. if s.params > 0 {
  163. if err := s.conn.readUntilEOF(); err != nil {
  164. return nil, errors.Trace(err)
  165. }
  166. }
  167. if s.columns > 0 {
  168. if err := s.conn.readUntilEOF(); err != nil {
  169. return nil, errors.Trace(err)
  170. }
  171. }
  172. return s, nil
  173. }