feat(db): 添加对达梦数据库的支持
- 在应用程序中新增对达梦数据库(DM)的配置和连接支持 - 实现 SetDmDB 函数以配置达梦数据库连接 - 更新数据库操作逻辑,支持达梦特有的 SQL 语法和功能 - 在相关文件中添加达梦数据库的处理逻辑,包括表创建、数据插入和查询 - 更新 go.mod 和 go.sum 文件以引入达梦数据库驱动 - 增强文档,详细说明达梦数据库的配置和使用方法
This commit is contained in:
+456
@@ -0,0 +1,456 @@
|
||||
/*
|
||||
* Copyright (c) 2000-2018, 达梦数据库有限公司.
|
||||
* All rights reserved.
|
||||
*/
|
||||
package dm
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"math/big"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
XDEC_MAX_PREC int = 40
|
||||
XDEC_SIZE = 21
|
||||
|
||||
FLAG_ZERO int = 0x80
|
||||
FLAG_POSITIVE int = 0xC1
|
||||
FLAG_NEGTIVE int = 0x3E
|
||||
POSITIVE_EXP_MAX = 0xff - FLAG_POSITIVE
|
||||
EXP_MAX int = 0xFF - 1 - FLAG_POSITIVE
|
||||
EXP_MIN int = FLAG_NEGTIVE + 1 - 0x7F
|
||||
|
||||
NUM_POSITIVE int = 1
|
||||
NUM_NEGTIVE int = 101
|
||||
)
|
||||
|
||||
type DmDecimal struct {
|
||||
sign int
|
||||
weight int
|
||||
prec int
|
||||
scale int
|
||||
digits string
|
||||
|
||||
Valid bool
|
||||
}
|
||||
|
||||
func NewDecimalFromInt64(x int64) (*DmDecimal, error) {
|
||||
return NewDecimalFromBigInt(big.NewInt(x))
|
||||
}
|
||||
|
||||
func (d DmDecimal) ToInt64() int64 {
|
||||
return d.ToBigInt().Int64()
|
||||
}
|
||||
|
||||
func NewDecimalFromFloat64(x float64) (*DmDecimal, error) {
|
||||
return NewDecimalFromBigFloat(big.NewFloat(x))
|
||||
}
|
||||
|
||||
func (d DmDecimal) ToFloat64() float64 {
|
||||
f, _ := d.ToBigFloat().Float64()
|
||||
return f
|
||||
}
|
||||
|
||||
func NewDecimalFromBigInt(bigInt *big.Int) (*DmDecimal, error) {
|
||||
return newDecimal(bigInt, len(bigInt.String()), 0)
|
||||
}
|
||||
|
||||
func (d DmDecimal) ToBigInt() *big.Int {
|
||||
if d.isZero() {
|
||||
return big.NewInt(0)
|
||||
}
|
||||
var digits = d.digits
|
||||
if d.sign < 0 {
|
||||
digits = "-" + digits
|
||||
}
|
||||
i1, ok := new(big.Int).SetString(digits, 10)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
if d.weight > 0 {
|
||||
i2, ok := new(big.Int).SetString("1"+strings.Repeat("0", d.weight), 10)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
i1.Mul(i1, i2)
|
||||
} else if d.weight < 0 {
|
||||
i2, ok := new(big.Int).SetString("1"+strings.Repeat("0", -d.weight), 10)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
i1.Quo(i1, i2)
|
||||
}
|
||||
return i1
|
||||
}
|
||||
|
||||
func NewDecimalFromBigFloat(bigFloat *big.Float) (*DmDecimal, error) {
|
||||
return newDecimal(bigFloat, int(bigFloat.Prec()), int(bigFloat.Prec()))
|
||||
}
|
||||
|
||||
func (d DmDecimal) ToBigFloat() *big.Float {
|
||||
if d.isZero() {
|
||||
return big.NewFloat(0.0)
|
||||
}
|
||||
var digits = d.digits
|
||||
if d.sign < 0 {
|
||||
digits = "-" + digits
|
||||
}
|
||||
f1, ok := new(big.Float).SetString(digits)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
if d.weight > 0 {
|
||||
f2, ok := new(big.Float).SetString("1" + strings.Repeat("0", d.weight))
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
f1.Mul(f1, f2)
|
||||
} else if d.weight < 0 {
|
||||
f2, ok := new(big.Float).SetString("1" + strings.Repeat("0", -d.weight))
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
f1.Quo(f1, f2)
|
||||
}
|
||||
return f1
|
||||
}
|
||||
|
||||
func NewDecimalFromString(s string) (*DmDecimal, error) {
|
||||
num, ok := new(big.Float).SetString(strings.TrimSpace(s))
|
||||
if !ok {
|
||||
return nil, ECGO_DATA_CONVERTION_ERROR.throw()
|
||||
}
|
||||
return NewDecimalFromBigFloat(num)
|
||||
}
|
||||
|
||||
func (d DmDecimal) String() string {
|
||||
|
||||
if d.isZero() {
|
||||
return "0"
|
||||
}
|
||||
digitsStr := d.digits
|
||||
if d.weight > 0 {
|
||||
digitsStr = digitsStr + strings.Repeat("0", d.weight)
|
||||
} else if d.weight < 0 {
|
||||
if len(digitsStr) < -d.weight {
|
||||
digitsStr = strings.Repeat("0", -d.weight-len(digitsStr)+1) + digitsStr
|
||||
}
|
||||
indexOfDot := len(digitsStr) + d.weight
|
||||
digitsStr = digitsStr[:indexOfDot] + "." + digitsStr[indexOfDot:]
|
||||
}
|
||||
|
||||
if digitsStr[0] == '0' && digitsStr[1] != '.' {
|
||||
digitsStr = digitsStr[1:]
|
||||
}
|
||||
|
||||
if digitsStr[len(digitsStr)-1] == '0' && strings.IndexRune(digitsStr, '.') >= 0 {
|
||||
digitsStr = digitsStr[0 : len(digitsStr)-1]
|
||||
}
|
||||
|
||||
if d.sign < 0 {
|
||||
digitsStr = "-" + digitsStr
|
||||
}
|
||||
|
||||
return digitsStr
|
||||
}
|
||||
|
||||
func (d DmDecimal) Sign() int {
|
||||
return d.sign
|
||||
}
|
||||
|
||||
func (dest *DmDecimal) Scan(src interface{}) error {
|
||||
if dest == nil {
|
||||
return ECGO_STORE_IN_NIL_POINTER.throw()
|
||||
}
|
||||
switch src := src.(type) {
|
||||
case nil:
|
||||
*dest = *new(DmDecimal)
|
||||
|
||||
(*dest).Valid = false
|
||||
return nil
|
||||
case int, int8, int16, int32, int64:
|
||||
d, err := NewDecimalFromInt64(reflect.ValueOf(src).Int())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*dest = *d
|
||||
return nil
|
||||
case uint, uint8, uint16, uint32, uint64:
|
||||
d, err := NewDecimalFromBigInt(new(big.Int).SetUint64(reflect.ValueOf(src).Uint()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*dest = *d
|
||||
return nil
|
||||
case float32, float64:
|
||||
d, err := NewDecimalFromFloat64(reflect.ValueOf(src).Float())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*dest = *d
|
||||
return nil
|
||||
case string:
|
||||
d, err := NewDecimalFromString(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*dest = *d
|
||||
return nil
|
||||
case *DmDecimal:
|
||||
*dest = *src
|
||||
return nil
|
||||
default:
|
||||
return UNSUPPORTED_SCAN
|
||||
}
|
||||
}
|
||||
|
||||
func (d DmDecimal) Value() (driver.Value, error) {
|
||||
if !d.Valid {
|
||||
return nil, nil
|
||||
}
|
||||
return d, nil
|
||||
}
|
||||
|
||||
func newDecimal(dec interface{}, prec int, scale int) (*DmDecimal, error) {
|
||||
d := &DmDecimal{
|
||||
prec: prec,
|
||||
scale: scale,
|
||||
Valid: true,
|
||||
}
|
||||
if isFloat(DECIMAL, scale) {
|
||||
d.prec = getFloatPrec(prec)
|
||||
d.scale = -1
|
||||
}
|
||||
switch de := dec.(type) {
|
||||
case *big.Int:
|
||||
d.sign = de.Sign()
|
||||
|
||||
if d.isZero() {
|
||||
return d, nil
|
||||
}
|
||||
str := de.String()
|
||||
|
||||
if d.sign < 0 {
|
||||
str = str[1:]
|
||||
}
|
||||
|
||||
if err := checkPrec(len(str), prec); err != nil {
|
||||
return d, err
|
||||
}
|
||||
i := 0
|
||||
istart := len(str) - 1
|
||||
|
||||
for i = istart; i > 0; i-- {
|
||||
if str[i] != '0' {
|
||||
break
|
||||
}
|
||||
}
|
||||
str = str[:i+1]
|
||||
d.weight += istart - i
|
||||
|
||||
if isOdd(d.weight) {
|
||||
str += "0"
|
||||
d.weight -= 1
|
||||
}
|
||||
if isOdd(len(str)) {
|
||||
str = "0" + str
|
||||
}
|
||||
d.digits = str
|
||||
case *big.Float:
|
||||
d.sign = de.Sign()
|
||||
|
||||
if d.isZero() {
|
||||
return d, nil
|
||||
}
|
||||
str := de.Text('f', -1)
|
||||
|
||||
if d.sign < 0 {
|
||||
str = str[1:]
|
||||
}
|
||||
|
||||
pointIndex := strings.IndexByte(str, '.')
|
||||
i, istart, length := 0, 0, len(str)
|
||||
|
||||
if pointIndex != -1 {
|
||||
if str[0] == '0' {
|
||||
|
||||
istart = 2
|
||||
for i = istart; i < length; i++ {
|
||||
if str[i] != '0' {
|
||||
break
|
||||
}
|
||||
}
|
||||
str = str[i:]
|
||||
d.weight -= i - istart + len(str)
|
||||
} else {
|
||||
str = str[:pointIndex] + str[pointIndex+1:]
|
||||
d.weight -= length - pointIndex - 1
|
||||
}
|
||||
}
|
||||
|
||||
length = len(str)
|
||||
istart = length - 1
|
||||
for i = istart; i > 0; i-- {
|
||||
if str[i] != '0' {
|
||||
break
|
||||
}
|
||||
}
|
||||
str = str[:i+1] + str[length:]
|
||||
d.weight += istart - i
|
||||
|
||||
if isOdd(d.weight) {
|
||||
str += "0"
|
||||
d.weight -= 1
|
||||
}
|
||||
if isOdd(len(str)) {
|
||||
str = "0" + str
|
||||
}
|
||||
d.digits = str
|
||||
case []byte:
|
||||
return decodeDecimal(de, prec, scale)
|
||||
}
|
||||
return d, nil
|
||||
}
|
||||
|
||||
func (d DmDecimal) encodeDecimal() ([]byte, error) {
|
||||
if d.isZero() {
|
||||
return []byte{byte(FLAG_ZERO)}, nil
|
||||
}
|
||||
exp := (d.weight+len(d.digits))/2 - 1
|
||||
|
||||
var realExpMax int
|
||||
|
||||
if d.sign == NUM_POSITIVE {
|
||||
realExpMax = POSITIVE_EXP_MAX
|
||||
} else {
|
||||
realExpMax = EXP_MAX
|
||||
}
|
||||
if exp > realExpMax || exp < EXP_MIN {
|
||||
return nil, ECGO_DATA_TOO_LONG.throw()
|
||||
}
|
||||
validLen := len(d.digits)/2 + 1
|
||||
|
||||
if d.sign < 0 && validLen >= XDEC_SIZE {
|
||||
validLen = XDEC_SIZE - 1
|
||||
} else if validLen > XDEC_SIZE {
|
||||
validLen = XDEC_SIZE
|
||||
}
|
||||
retLen := validLen
|
||||
if d.sign < 0 {
|
||||
retLen = validLen + 1
|
||||
}
|
||||
retBytes := make([]byte, retLen)
|
||||
if d.sign > 0 {
|
||||
retBytes[0] = byte(exp + FLAG_POSITIVE)
|
||||
} else {
|
||||
retBytes[0] = byte(FLAG_NEGTIVE - exp)
|
||||
}
|
||||
|
||||
ibytes := 1
|
||||
for ichar := 0; ibytes < validLen; {
|
||||
digit1, err := strconv.Atoi(string(d.digits[ichar]))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ichar++
|
||||
digit2, err := strconv.Atoi(string(d.digits[ichar]))
|
||||
ichar++
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
digit := digit1*10 + digit2
|
||||
if d.sign > 0 {
|
||||
retBytes[ibytes] = byte(digit + NUM_POSITIVE)
|
||||
} else {
|
||||
retBytes[ibytes] = byte(NUM_NEGTIVE - digit)
|
||||
}
|
||||
ibytes++
|
||||
}
|
||||
if d.sign < 0 && ibytes < retLen {
|
||||
retBytes[ibytes] = 0x66
|
||||
ibytes++
|
||||
}
|
||||
if ibytes < retLen {
|
||||
retBytes[ibytes] = 0x00
|
||||
}
|
||||
return retBytes, nil
|
||||
}
|
||||
|
||||
func decodeDecimal(values []byte, prec int, scale int) (*DmDecimal, error) {
|
||||
var decimal = &DmDecimal{
|
||||
prec: prec,
|
||||
scale: scale,
|
||||
sign: 0,
|
||||
weight: 0,
|
||||
Valid: true,
|
||||
}
|
||||
if values == nil || len(values) == 0 || len(values) > XDEC_SIZE {
|
||||
return nil, ECGO_FATAL_ERROR.throw()
|
||||
}
|
||||
if values[0] == byte(FLAG_ZERO) || len(values) == 1 {
|
||||
return decimal, nil
|
||||
}
|
||||
if values[0]&byte(FLAG_ZERO) != 0 {
|
||||
decimal.sign = 1
|
||||
} else {
|
||||
decimal.sign = -1
|
||||
}
|
||||
|
||||
var flag = int(Dm_build_1346.Dm_build_1466(values, 0))
|
||||
var exp int
|
||||
if decimal.sign > 0 {
|
||||
exp = flag - FLAG_POSITIVE
|
||||
} else {
|
||||
exp = FLAG_NEGTIVE - flag
|
||||
}
|
||||
var digit = 0
|
||||
var sf = ""
|
||||
for ival := 1; ival < len(values); ival++ {
|
||||
if decimal.sign > 0 {
|
||||
digit = int(values[ival]) - NUM_POSITIVE
|
||||
} else {
|
||||
digit = NUM_NEGTIVE - int(values[ival])
|
||||
}
|
||||
if digit < 0 || digit > 99 {
|
||||
break
|
||||
}
|
||||
if digit < 10 {
|
||||
sf += "0"
|
||||
}
|
||||
sf += strconv.Itoa(digit)
|
||||
}
|
||||
decimal.digits = sf
|
||||
decimal.weight = exp*2 - (len(decimal.digits) - 2)
|
||||
|
||||
return decimal, nil
|
||||
}
|
||||
|
||||
func (d DmDecimal) isZero() bool {
|
||||
return d.sign == 0
|
||||
}
|
||||
|
||||
func checkPrec(len int, prec int) error {
|
||||
if prec > 0 && len > prec || len > XDEC_MAX_PREC {
|
||||
return ECGO_DATA_TOO_LONG.throw()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func isOdd(val int) bool {
|
||||
return val%2 != 0
|
||||
}
|
||||
|
||||
func (d *DmDecimal) checkValid() error {
|
||||
if !d.Valid {
|
||||
return ECGO_IS_NULL.throw()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DmDecimal) GormDataType() string {
|
||||
return "DECIMAL"
|
||||
}
|
||||
Reference in New Issue
Block a user