| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167 |
- // +build go1.9
- package mssql
- import (
- "bytes"
- "encoding/binary"
- "errors"
- "fmt"
- "reflect"
- "time"
- )
- var (
- ErrorEmptyTVPName = errors.New("TVPTypeName must not be empty")
- ErrorTVPTypeSlice = errors.New("TVPType must be slice type")
- ErrorTVPTypeSliceIsEmpty = errors.New("TVPType mustn't be null value")
- )
- //TVPType is driver type, which allows supporting Table Valued Parameters (TVP) in SQL Server
- type TVPType struct {
- //TVP param name, mustn't be default value
- TVPTypeName string
- //TVP scheme name
- TVPScheme string
- //TVP Value. Param must be the slice, mustn't be nil
- TVPValue interface{}
- }
- func (tvp TVPType) check() error {
- if len(tvp.TVPTypeName) == 0 {
- return ErrorEmptyTVPName
- }
- valueOf := reflect.ValueOf(tvp.TVPValue)
- if valueOf.Kind() != reflect.Slice {
- return ErrorTVPTypeSlice
- }
- if valueOf.IsNil() {
- return ErrorTVPTypeSliceIsEmpty
- }
- if reflect.TypeOf(tvp.TVPValue).Elem().Kind() != reflect.Struct {
- return ErrorTVPTypeSlice
- }
- return nil
- }
- func (tvp TVPType) encode() ([]byte, error) {
- columnStr, err := tvp.columnTypes()
- if err != nil {
- return nil, err
- }
- preparedBuffer := make([]byte, 0, 20+(10*len(columnStr)))
- buf := bytes.NewBuffer(preparedBuffer)
- err = writeBVarChar(buf, "")
- if err != nil {
- return nil, err
- }
- writeBVarChar(buf, tvp.TVPScheme)
- writeBVarChar(buf, tvp.TVPTypeName)
- binary.Write(buf, binary.LittleEndian, uint16(len(columnStr)))
- for i, column := range columnStr {
- binary.Write(buf, binary.LittleEndian, uint32(column.UserType))
- binary.Write(buf, binary.LittleEndian, uint16(column.Flags))
- writeTypeInfo(buf, &columnStr[i].ti)
- writeBVarChar(buf, "")
- }
- buf.WriteByte(_TVP_END_TOKEN)
- conn := new(Conn)
- conn.sess = new(tdsSession)
- conn.sess.loginAck = loginAckStruct{TDSVersion: verTDS73}
- stmt := &Stmt{
- c: conn,
- }
- val := reflect.ValueOf(tvp.TVPValue)
- for i := 0; i < val.Len(); i++ {
- refStr := reflect.ValueOf(val.Index(i).Interface())
- buf.WriteByte(_TVP_ROW_TOKEN)
- for j := 0; j < refStr.NumField(); j++ {
- field := refStr.Field(j)
- tvpVal := field.Interface()
- valOf := reflect.ValueOf(tvpVal)
- elemKind := field.Kind()
- if elemKind == reflect.Ptr && valOf.IsNil() {
- switch tvpVal.(type) {
- case *bool, *time.Time, *int8, *int16, *int32, *int64, *float32, *float64:
- binary.Write(buf, binary.LittleEndian, uint8(0))
- continue
- default:
- binary.Write(buf, binary.LittleEndian, uint64(_PLP_NULL))
- continue
- }
- }
- if elemKind == reflect.Slice && valOf.IsNil() {
- binary.Write(buf, binary.LittleEndian, uint64(_PLP_NULL))
- continue
- }
- cval, err := convertInputParameter(tvpVal)
- if err != nil {
- return nil, fmt.Errorf("failed to convert tvp parameter row col: %s", err)
- }
- param, err := stmt.makeParam(cval)
- if err != nil {
- return nil, fmt.Errorf("failed to make tvp parameter row col: %s", err)
- }
- columnStr[j].ti.Writer(buf, param.ti, param.buffer)
- }
- }
- buf.WriteByte(_TVP_END_TOKEN)
- return buf.Bytes(), nil
- }
- func (tvp TVPType) columnTypes() ([]columnStruct, error) {
- val := reflect.ValueOf(tvp.TVPValue)
- var firstRow interface{}
- if val.Len() != 0 {
- firstRow = val.Index(0).Interface()
- } else {
- firstRow = reflect.New(reflect.TypeOf(tvp.TVPValue).Elem()).Elem().Interface()
- }
- tvpRow := reflect.TypeOf(firstRow)
- columnCount := tvpRow.NumField()
- defaultValues := make([]interface{}, 0, columnCount)
- for i := 0; i < columnCount; i++ {
- typeField := tvpRow.Field(i).Type
- if typeField.Kind() == reflect.Ptr {
- v := reflect.New(typeField.Elem())
- defaultValues = append(defaultValues, v.Interface())
- continue
- }
- defaultValues = append(defaultValues, reflect.Zero(typeField).Interface())
- }
- conn := new(Conn)
- conn.sess = new(tdsSession)
- conn.sess.loginAck = loginAckStruct{TDSVersion: verTDS73}
- stmt := &Stmt{
- c: conn,
- }
- columnConfiguration := make([]columnStruct, 0, columnCount)
- for index, val := range defaultValues {
- cval, err := convertInputParameter(val)
- if err != nil {
- return nil, fmt.Errorf("failed to convert tvp parameter row %d col %d: %s", index, val, err)
- }
- param, err := stmt.makeParam(cval)
- if err != nil {
- return nil, err
- }
- column := columnStruct{
- ti: param.ti,
- }
- switch param.ti.TypeId {
- case typeNVarChar, typeBigVarBin:
- column.ti.Size = 0
- }
- columnConfiguration = append(columnConfiguration, column)
- }
- return columnConfiguration, nil
- }
|