func RegisterTransaction(t TransactionType, tx Transaction) {
	if tx == nil {
		panic(ErrTransactionRegistration)
	}
	rt := reflect.TypeOf(tx)
	if rt == txWrapperType {
		panic(ErrTransactionRegistration)
	}
	txTypeMapping.Store(t, rt)
}
RegisterTransaction registers transaction type to wrapper.
package interfaces
import (
	"encoding/json"
	"reflect"
	"sync"
	"github.com/pkg/errors"
	"github.com/ugorji/go/codec"
	"github.com/SQLess/SQLess/utils"
)
const (
	valueTypeMap   = 9
	valueTypeArray = 10
)
var (
	txTypeMapping sync.Map
	txType        = reflect.TypeOf((*Transaction)(nil)).Elem()
	txWrapperType = reflect.TypeOf((*TransactionWrapper)(nil))
	ErrInvalidContainerType = errors.New("invalid container type for TransactionWrapper")
	ErrInvalidTransactionType = errors.New("invalid transaction type, can not instantiate transaction")
	ErrTransactionRegistration = errors.New("transaction register failed")
	ErrMsgPackVersionMismatch = errors.New("msgpack library version mismatch")
)
func init() {
	if codec.GenVersion < 8 {
		panic(ErrMsgPackVersionMismatch)
	}
	if err := utils.RegisterInterfaceToMsgPack(txType, txWrapperType); err != nil {
		panic(err)
	}
}
type TransactionWrapper struct {
	Transaction
}
func (w *TransactionWrapper) Unwrap() Transaction {
	return w.Transaction
}
func (w *TransactionWrapper) CodecEncodeSelf(e *codec.Encoder) {
	helperEncoder, encDriver := codec.GenHelperEncoder(e)
	if w == nil || w.Transaction == nil {
		encDriver.EncodeNil()
		return
	}
	encDriver.WriteArrayStart(2)
	encDriver.WriteArrayElem()
	encDriver.EncodeUint(uint64(w.GetTransactionType()))
	encDriver.WriteArrayElem()
	helperEncoder.EncFallback(w.Transaction)
	encDriver.WriteArrayEnd()
}
func (w *TransactionWrapper) CodecDecodeSelf(d *codec.Decoder) {
	_, decodeDriver := codec.GenHelperDecoder(d)
	w.Transaction = nil
	ct := decodeDriver.ContainerType()
	switch ct {
	case valueTypeArray:
		w.decodeFromWrapper(d)
	case valueTypeMap:
		w.decodeFromRaw(d)
	default:
		panic(errors.Wrapf(ErrInvalidContainerType, "type %v applied", ct))
	}
}
func (w TransactionWrapper) MarshalJSON() ([]byte, error) {
	return json.Marshal(w.Transaction)
}
func (w *TransactionWrapper) UnmarshalJSON(data []byte) (err error) {
	var typeDetector TransactionTypeMixin
	typeDetector.SetTransactionType(TransactionTypeNumber)
	if err = json.Unmarshal(data, &typeDetector); err != nil {
		err = errors.Wrap(err, "try decode transaction failed")
		return
	}
	txType := typeDetector.GetTransactionType()
	if txType == TransactionTypeNumber {
		err = errors.Wrapf(ErrInvalidTransactionType, "invalid tx type: %d", txType)
		return
	}
	if w.Transaction, err = NewTransaction(txType); err != nil {
		err = errors.Wrapf(err, "instantiate transaction type %s failed", txType.String())
		return
	}
	return json.Unmarshal(data, &w.Transaction)
}
func (w *TransactionWrapper) decodeFromWrapper(d *codec.Decoder) {
	helperDecoder, decodeDriver := codec.GenHelperDecoder(d)
	containerLen := decodeDriver.ReadArrayStart()
	for i := 0; i < containerLen; i++ {
		if decodeDriver.CheckBreak() {
			break
		}
		decodeDriver.ReadArrayElem()
		if i == 0 {
			if decodeDriver.TryDecodeAsNil() {
				panic(ErrInvalidTransactionType)
			} else {
				var txType TransactionType
				helperDecoder.DecFallback(&txType, true)
				var err error
				if w.Transaction, err = NewTransaction(txType); err != nil {
					panic(err)
				}
			}
		} else if i == 1 {
			if ct := decodeDriver.ContainerType(); ct != valueTypeMap {
				panic(errors.Wrapf(ErrInvalidContainerType, "type %v applied", ct))
			}
			if !decodeDriver.TryDecodeAsNil() {
				helperDecoder.DecFallback(&w.Transaction, true)
			}
		} else {
			helperDecoder.DecStructFieldNotFound(i, "")
		}
	}
	decodeDriver.ReadArrayEnd()
	if containerLen < 2 {
		panic(ErrInvalidTransactionType)
	}
}
func (w *TransactionWrapper) decodeFromRaw(d *codec.Decoder) {
	helperDecoder, _ := codec.GenHelperDecoder(d)
	rawBytes := helperDecoder.DecRaw()
	var typeDetector TransactionTypeMixin
	typeDetector.SetTransactionType(TransactionTypeNumber)
	var err error
	if err = utils.DecodeMsgPack(rawBytes, &typeDetector); err != nil {
		panic(err)
	}
	txType := typeDetector.GetTransactionType()
	if txType == TransactionTypeNumber {
		panic(ErrInvalidTransactionType)
	}
	if w.Transaction, err = NewTransaction(txType); err != nil {
		panic(err)
	}
	if err = utils.DecodeMsgPack(rawBytes, w.Transaction); err != nil {
		panic(err)
	}
}