rowdata.go 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. package mysql
  2. import (
  3. "strconv"
  4. "github.com/go-mysql-org/go-mysql/utils"
  5. "github.com/pingcap/errors"
  6. )
  7. type RowData []byte
  8. func (p RowData) Parse(f []*Field, binary bool, dst []FieldValue) ([]FieldValue, error) {
  9. if binary {
  10. return p.ParseBinary(f, dst)
  11. } else {
  12. return p.ParseText(f, dst)
  13. }
  14. }
  15. func (p RowData) ParseText(f []*Field, dst []FieldValue) ([]FieldValue, error) {
  16. for len(dst) < len(f) {
  17. dst = append(dst, FieldValue{})
  18. }
  19. data := dst[:len(f)]
  20. var err error
  21. var v []byte
  22. var isNull bool
  23. var pos, n int
  24. for i := range f {
  25. v, isNull, n, err = LengthEncodedString(p[pos:])
  26. if err != nil {
  27. return nil, errors.Trace(err)
  28. }
  29. pos += n
  30. if isNull {
  31. data[i].Type = FieldValueTypeNull
  32. } else {
  33. isUnsigned := f[i].Flag&UNSIGNED_FLAG != 0
  34. switch f[i].Type {
  35. case MYSQL_TYPE_TINY, MYSQL_TYPE_SHORT, MYSQL_TYPE_INT24,
  36. MYSQL_TYPE_LONGLONG, MYSQL_TYPE_LONG, MYSQL_TYPE_YEAR:
  37. if isUnsigned {
  38. var val uint64
  39. data[i].Type = FieldValueTypeUnsigned
  40. val, err = strconv.ParseUint(utils.ByteSliceToString(v), 10, 64)
  41. data[i].value = val
  42. } else {
  43. var val int64
  44. data[i].Type = FieldValueTypeSigned
  45. val, err = strconv.ParseInt(utils.ByteSliceToString(v), 10, 64)
  46. data[i].value = utils.Int64ToUint64(val)
  47. }
  48. case MYSQL_TYPE_FLOAT, MYSQL_TYPE_DOUBLE:
  49. var val float64
  50. data[i].Type = FieldValueTypeFloat
  51. val, err = strconv.ParseFloat(utils.ByteSliceToString(v), 64)
  52. data[i].value = utils.Float64ToUint64(val)
  53. default:
  54. data[i].Type = FieldValueTypeString
  55. data[i].str = append(data[i].str[:0], v...)
  56. }
  57. if err != nil {
  58. return nil, errors.Trace(err)
  59. }
  60. }
  61. }
  62. return data, nil
  63. }
  64. // ParseBinary parses the binary format of data
  65. // see https://dev.mysql.com/doc/internals/en/binary-protocol-value.html
  66. func (p RowData) ParseBinary(f []*Field, dst []FieldValue) ([]FieldValue, error) {
  67. for len(dst) < len(f) {
  68. dst = append(dst, FieldValue{})
  69. }
  70. data := dst[:len(f)]
  71. if p[0] != OK_HEADER {
  72. return nil, ErrMalformPacket
  73. }
  74. pos := 1 + ((len(f) + 7 + 2) >> 3)
  75. nullBitmap := p[1:pos]
  76. var isNull bool
  77. var n int
  78. var err error
  79. var v []byte
  80. for i := range data {
  81. if nullBitmap[(i+2)/8]&(1<<(uint(i+2)%8)) > 0 {
  82. data[i].Type = FieldValueTypeNull
  83. continue
  84. }
  85. isUnsigned := f[i].Flag&UNSIGNED_FLAG != 0
  86. switch f[i].Type {
  87. case MYSQL_TYPE_NULL:
  88. data[i].Type = FieldValueTypeNull
  89. continue
  90. case MYSQL_TYPE_TINY:
  91. if isUnsigned {
  92. v := ParseBinaryUint8(p[pos : pos+1])
  93. data[i].Type = FieldValueTypeUnsigned
  94. data[i].value = uint64(v)
  95. } else {
  96. v := ParseBinaryInt8(p[pos : pos+1])
  97. data[i].Type = FieldValueTypeSigned
  98. data[i].value = utils.Int64ToUint64(int64(v))
  99. }
  100. pos++
  101. continue
  102. case MYSQL_TYPE_SHORT, MYSQL_TYPE_YEAR:
  103. if isUnsigned {
  104. v := ParseBinaryUint16(p[pos : pos+2])
  105. data[i].Type = FieldValueTypeUnsigned
  106. data[i].value = uint64(v)
  107. } else {
  108. v := ParseBinaryInt16(p[pos : pos+2])
  109. data[i].Type = FieldValueTypeSigned
  110. data[i].value = utils.Int64ToUint64(int64(v))
  111. }
  112. pos += 2
  113. continue
  114. case MYSQL_TYPE_INT24, MYSQL_TYPE_LONG:
  115. if isUnsigned {
  116. v := ParseBinaryUint32(p[pos : pos+4])
  117. data[i].Type = FieldValueTypeUnsigned
  118. data[i].value = uint64(v)
  119. } else {
  120. v := ParseBinaryInt32(p[pos : pos+4])
  121. data[i].Type = FieldValueTypeSigned
  122. data[i].value = utils.Int64ToUint64(int64(v))
  123. }
  124. pos += 4
  125. continue
  126. case MYSQL_TYPE_LONGLONG:
  127. if isUnsigned {
  128. v := ParseBinaryUint64(p[pos : pos+8])
  129. data[i].Type = FieldValueTypeUnsigned
  130. data[i].value = v
  131. } else {
  132. v := ParseBinaryInt64(p[pos : pos+8])
  133. data[i].Type = FieldValueTypeSigned
  134. data[i].value = utils.Int64ToUint64(v)
  135. }
  136. pos += 8
  137. continue
  138. case MYSQL_TYPE_FLOAT:
  139. v := ParseBinaryFloat32(p[pos : pos+4])
  140. data[i].Type = FieldValueTypeFloat
  141. data[i].value = utils.Float64ToUint64(float64(v))
  142. pos += 4
  143. continue
  144. case MYSQL_TYPE_DOUBLE:
  145. v := ParseBinaryFloat64(p[pos : pos+8])
  146. data[i].Type = FieldValueTypeFloat
  147. data[i].value = utils.Float64ToUint64(v)
  148. pos += 8
  149. continue
  150. case MYSQL_TYPE_DECIMAL, MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_VARCHAR,
  151. MYSQL_TYPE_BIT, MYSQL_TYPE_ENUM, MYSQL_TYPE_SET, MYSQL_TYPE_TINY_BLOB,
  152. MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, MYSQL_TYPE_BLOB,
  153. MYSQL_TYPE_VAR_STRING, MYSQL_TYPE_STRING, MYSQL_TYPE_GEOMETRY, MYSQL_TYPE_JSON:
  154. v, isNull, n, err = LengthEncodedString(p[pos:])
  155. pos += n
  156. if err != nil {
  157. return nil, errors.Trace(err)
  158. }
  159. if !isNull {
  160. data[i].Type = FieldValueTypeString
  161. data[i].str = append(data[i].str[:0], v...)
  162. continue
  163. } else {
  164. data[i].Type = FieldValueTypeNull
  165. continue
  166. }
  167. case MYSQL_TYPE_DATE, MYSQL_TYPE_NEWDATE:
  168. var num uint64
  169. num, isNull, n = LengthEncodedInt(p[pos:])
  170. pos += n
  171. if isNull {
  172. data[i].Type = FieldValueTypeNull
  173. continue
  174. }
  175. data[i].Type = FieldValueTypeString
  176. data[i].str, err = FormatBinaryDate(int(num), p[pos:])
  177. pos += int(num)
  178. if err != nil {
  179. return nil, errors.Trace(err)
  180. }
  181. case MYSQL_TYPE_TIMESTAMP, MYSQL_TYPE_DATETIME:
  182. var num uint64
  183. num, isNull, n = LengthEncodedInt(p[pos:])
  184. pos += n
  185. if isNull {
  186. data[i].Type = FieldValueTypeNull
  187. continue
  188. }
  189. data[i].Type = FieldValueTypeString
  190. data[i].str, err = FormatBinaryDateTime(int(num), p[pos:])
  191. pos += int(num)
  192. if err != nil {
  193. return nil, errors.Trace(err)
  194. }
  195. case MYSQL_TYPE_TIME:
  196. var num uint64
  197. num, isNull, n = LengthEncodedInt(p[pos:])
  198. pos += n
  199. if isNull {
  200. data[i].Type = FieldValueTypeNull
  201. continue
  202. }
  203. data[i].Type = FieldValueTypeString
  204. data[i].str, err = FormatBinaryTime(int(num), p[pos:])
  205. pos += int(num)
  206. if err != nil {
  207. return nil, errors.Trace(err)
  208. }
  209. default:
  210. return nil, errors.Errorf("Stmt Unknown FieldType %d %s", f[i].Type, f[i].Name)
  211. }
  212. }
  213. return data, nil
  214. }