tvp_go19.go 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. // +build go1.9
  2. package mssql
  3. import (
  4. "bytes"
  5. "encoding/binary"
  6. "errors"
  7. "fmt"
  8. "reflect"
  9. "time"
  10. )
  11. var (
  12. ErrorEmptyTVPName = errors.New("TVPTypeName must not be empty")
  13. ErrorTVPTypeSlice = errors.New("TVPType must be slice type")
  14. ErrorTVPTypeSliceIsEmpty = errors.New("TVPType mustn't be null value")
  15. )
  16. //TVPType is driver type, which allows supporting Table Valued Parameters (TVP) in SQL Server
  17. type TVPType struct {
  18. //TVP param name, mustn't be default value
  19. TVPTypeName string
  20. //TVP scheme name
  21. TVPScheme string
  22. //TVP Value. Param must be the slice, mustn't be nil
  23. TVPValue interface{}
  24. }
  25. func (tvp TVPType) check() error {
  26. if len(tvp.TVPTypeName) == 0 {
  27. return ErrorEmptyTVPName
  28. }
  29. valueOf := reflect.ValueOf(tvp.TVPValue)
  30. if valueOf.Kind() != reflect.Slice {
  31. return ErrorTVPTypeSlice
  32. }
  33. if valueOf.IsNil() {
  34. return ErrorTVPTypeSliceIsEmpty
  35. }
  36. if reflect.TypeOf(tvp.TVPValue).Elem().Kind() != reflect.Struct {
  37. return ErrorTVPTypeSlice
  38. }
  39. return nil
  40. }
  41. func (tvp TVPType) encode() ([]byte, error) {
  42. columnStr, err := tvp.columnTypes()
  43. if err != nil {
  44. return nil, err
  45. }
  46. preparedBuffer := make([]byte, 0, 20+(10*len(columnStr)))
  47. buf := bytes.NewBuffer(preparedBuffer)
  48. err = writeBVarChar(buf, "")
  49. if err != nil {
  50. return nil, err
  51. }
  52. writeBVarChar(buf, tvp.TVPScheme)
  53. writeBVarChar(buf, tvp.TVPTypeName)
  54. binary.Write(buf, binary.LittleEndian, uint16(len(columnStr)))
  55. for i, column := range columnStr {
  56. binary.Write(buf, binary.LittleEndian, uint32(column.UserType))
  57. binary.Write(buf, binary.LittleEndian, uint16(column.Flags))
  58. writeTypeInfo(buf, &columnStr[i].ti)
  59. writeBVarChar(buf, "")
  60. }
  61. buf.WriteByte(_TVP_END_TOKEN)
  62. conn := new(Conn)
  63. conn.sess = new(tdsSession)
  64. conn.sess.loginAck = loginAckStruct{TDSVersion: verTDS73}
  65. stmt := &Stmt{
  66. c: conn,
  67. }
  68. val := reflect.ValueOf(tvp.TVPValue)
  69. for i := 0; i < val.Len(); i++ {
  70. refStr := reflect.ValueOf(val.Index(i).Interface())
  71. buf.WriteByte(_TVP_ROW_TOKEN)
  72. for j := 0; j < refStr.NumField(); j++ {
  73. field := refStr.Field(j)
  74. tvpVal := field.Interface()
  75. valOf := reflect.ValueOf(tvpVal)
  76. elemKind := field.Kind()
  77. if elemKind == reflect.Ptr && valOf.IsNil() {
  78. switch tvpVal.(type) {
  79. case *bool, *time.Time, *int8, *int16, *int32, *int64, *float32, *float64:
  80. binary.Write(buf, binary.LittleEndian, uint8(0))
  81. continue
  82. default:
  83. binary.Write(buf, binary.LittleEndian, uint64(_PLP_NULL))
  84. continue
  85. }
  86. }
  87. if elemKind == reflect.Slice && valOf.IsNil() {
  88. binary.Write(buf, binary.LittleEndian, uint64(_PLP_NULL))
  89. continue
  90. }
  91. cval, err := convertInputParameter(tvpVal)
  92. if err != nil {
  93. return nil, fmt.Errorf("failed to convert tvp parameter row col: %s", err)
  94. }
  95. param, err := stmt.makeParam(cval)
  96. if err != nil {
  97. return nil, fmt.Errorf("failed to make tvp parameter row col: %s", err)
  98. }
  99. columnStr[j].ti.Writer(buf, param.ti, param.buffer)
  100. }
  101. }
  102. buf.WriteByte(_TVP_END_TOKEN)
  103. return buf.Bytes(), nil
  104. }
  105. func (tvp TVPType) columnTypes() ([]columnStruct, error) {
  106. val := reflect.ValueOf(tvp.TVPValue)
  107. var firstRow interface{}
  108. if val.Len() != 0 {
  109. firstRow = val.Index(0).Interface()
  110. } else {
  111. firstRow = reflect.New(reflect.TypeOf(tvp.TVPValue).Elem()).Elem().Interface()
  112. }
  113. tvpRow := reflect.TypeOf(firstRow)
  114. columnCount := tvpRow.NumField()
  115. defaultValues := make([]interface{}, 0, columnCount)
  116. for i := 0; i < columnCount; i++ {
  117. typeField := tvpRow.Field(i).Type
  118. if typeField.Kind() == reflect.Ptr {
  119. v := reflect.New(typeField.Elem())
  120. defaultValues = append(defaultValues, v.Interface())
  121. continue
  122. }
  123. defaultValues = append(defaultValues, reflect.Zero(typeField).Interface())
  124. }
  125. conn := new(Conn)
  126. conn.sess = new(tdsSession)
  127. conn.sess.loginAck = loginAckStruct{TDSVersion: verTDS73}
  128. stmt := &Stmt{
  129. c: conn,
  130. }
  131. columnConfiguration := make([]columnStruct, 0, columnCount)
  132. for index, val := range defaultValues {
  133. cval, err := convertInputParameter(val)
  134. if err != nil {
  135. return nil, fmt.Errorf("failed to convert tvp parameter row %d col %d: %s", index, val, err)
  136. }
  137. param, err := stmt.makeParam(cval)
  138. if err != nil {
  139. return nil, err
  140. }
  141. column := columnStruct{
  142. ti: param.ti,
  143. }
  144. switch param.ti.TypeId {
  145. case typeNVarChar, typeBigVarBin:
  146. column.ti.Size = 0
  147. }
  148. columnConfiguration = append(columnConfiguration, column)
  149. }
  150. return columnConfiguration, nil
  151. }