7f7b585ffb
- 在应用程序中新增对达梦数据库(DM)的配置和连接支持 - 实现 SetDmDB 函数以配置达梦数据库连接 - 更新数据库操作逻辑,支持达梦特有的 SQL 语法和功能 - 在相关文件中添加达梦数据库的处理逻辑,包括表创建、数据插入和查询 - 更新 go.mod 和 go.sum 文件以引入达梦数据库驱动 - 增强文档,详细说明达梦数据库的配置和使用方法
451 lines
7.8 KiB
Go
451 lines
7.8 KiB
Go
/*
|
|
* Copyright (c) 2000-2018, 达梦数据库有限公司.
|
|
* All rights reserved.
|
|
*/
|
|
package dm
|
|
|
|
import (
|
|
"database/sql"
|
|
"database/sql/driver"
|
|
"math"
|
|
"reflect"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
const (
|
|
INT8_MAX int8 = math.MaxInt8
|
|
|
|
INT8_MIN int8 = math.MinInt8
|
|
|
|
BYTE_MAX byte = math.MaxUint8
|
|
|
|
BYTE_MIN byte = 0
|
|
|
|
INT16_MAX int16 = math.MaxInt16
|
|
|
|
INT16_MIN int16 = math.MinInt16
|
|
|
|
UINT16_MAX uint16 = math.MaxUint16
|
|
|
|
UINT16_MIN uint16 = 0
|
|
|
|
INT32_MAX int32 = math.MaxInt32
|
|
|
|
INT32_MIN int32 = math.MinInt32
|
|
|
|
UINT32_MAX uint32 = math.MaxUint32
|
|
|
|
UINT32_MIN uint32 = 0
|
|
|
|
INT64_MAX int64 = math.MaxInt64
|
|
|
|
INT64_MIN int64 = math.MinInt64
|
|
|
|
UINT64_MAX uint64 = math.MaxUint64
|
|
|
|
UINT64_MIN uint64 = 0
|
|
|
|
FLOAT32_MAX float32 = 3.4e+38
|
|
|
|
FLOAT32_MIN float32 = -3.4e+38
|
|
|
|
BYTE_SIZE = 1
|
|
|
|
USINT_SIZE = 2
|
|
|
|
ULINT_SIZE = 4
|
|
|
|
DDWORD_SIZE = 8
|
|
|
|
LINT64_SIZE = 8
|
|
|
|
CHAR = 0
|
|
|
|
VARCHAR2 = 1
|
|
|
|
VARCHAR = 2
|
|
|
|
BIT = 3
|
|
|
|
TINYINT = 5
|
|
|
|
SMALLINT = 6
|
|
|
|
INT = 7
|
|
|
|
BIGINT = 8
|
|
|
|
DECIMAL = 9
|
|
|
|
REAL = 10
|
|
|
|
DOUBLE = 11
|
|
|
|
BLOB = 12
|
|
|
|
BOOLEAN = 13
|
|
|
|
DATE = 14
|
|
|
|
TIME = 15
|
|
|
|
DATETIME = 16
|
|
|
|
BINARY = 17
|
|
|
|
VARBINARY = 18
|
|
|
|
CLOB = 19
|
|
|
|
INTERVAL_YM = 20
|
|
|
|
INTERVAL_DT = 21
|
|
|
|
TIME_TZ = 22
|
|
|
|
DATETIME_TZ = 23
|
|
|
|
XDEC_INT32 = 24
|
|
|
|
XDEC_INT64 = 25
|
|
|
|
DATETIME2 = 26
|
|
|
|
DATETIME2_TZ = 27
|
|
|
|
NULL = 28
|
|
|
|
ANY = 31
|
|
|
|
STAR_ALL = 32
|
|
|
|
STAR = 33
|
|
|
|
RECORD = 40
|
|
|
|
TYPE = 41
|
|
|
|
TYPE_REF = 42
|
|
|
|
UNKNOWN = 54
|
|
|
|
ARRAY = 117
|
|
|
|
CLASS = 119
|
|
|
|
CURSOR = 120
|
|
|
|
PLTYPE_RECORD = 121
|
|
|
|
SARRAY = 122
|
|
|
|
CURSOR_ORACLE = -10
|
|
|
|
BIT_PREC = BYTE_SIZE
|
|
|
|
TINYINT_PREC = BYTE_SIZE
|
|
|
|
SMALLINT_PREC = USINT_SIZE
|
|
|
|
INT_PREC = ULINT_SIZE
|
|
|
|
BIGINT_PREC = LINT64_SIZE
|
|
|
|
REAL_PREC = 4
|
|
|
|
DOUBLE_PREC = 8
|
|
|
|
DATE_PREC = 3
|
|
|
|
TIME_PREC = 5
|
|
|
|
DATETIME_PREC = 8
|
|
|
|
DATETIME2_PREC = 9
|
|
|
|
TIME_TZ_PREC = TIME_PREC + 2
|
|
|
|
DATETIME_TZ_PREC = DATETIME_PREC + 2
|
|
|
|
DATETIME2_TZ_PREC = DATETIME2_PREC + 2
|
|
|
|
INTERVAL_YM_PREC = 3 * ULINT_SIZE
|
|
|
|
INTERVAL_DT_PREC = 6 * ULINT_SIZE
|
|
|
|
VARCHAR_PREC = 8188
|
|
|
|
VARBINARY_PREC = 8188
|
|
|
|
BLOB_PREC int32 = INT32_MAX
|
|
|
|
CLOB_PREC int32 = INT32_MAX
|
|
|
|
NULL_PREC = 0
|
|
|
|
LOCAL_TIME_ZONE_SCALE_MASK = 0x00001000
|
|
|
|
BFILE_PREC = 512
|
|
|
|
BFILE_SCALE = 6
|
|
|
|
COMPLEX_SCALE = 5
|
|
|
|
CURRENCY_PREC = 19
|
|
|
|
CURRENCY_SCALE = 4
|
|
|
|
LOCAL_DATETIME_SCALE_MASK int32 = 0x00001000
|
|
|
|
ORACLE_FLOAT_SCALE_MASK int32 = 0x81
|
|
|
|
ORACLE_DATE_SCALE_MASK int32 = 0x00002000
|
|
)
|
|
|
|
func isComplexType(colType int, scale int) bool {
|
|
return (colType == BLOB && scale == COMPLEX_SCALE) || colType == ARRAY || colType == SARRAY || colType == CLASS || colType == PLTYPE_RECORD
|
|
}
|
|
|
|
func isLocalTimeZone(colType int, scale int) bool {
|
|
return (colType == DATETIME || colType == DATETIME2) && (scale&LOCAL_TIME_ZONE_SCALE_MASK) != 0
|
|
}
|
|
|
|
func getLocalTimeZoneScale(colType int, scale int) int {
|
|
return scale & (^LOCAL_TIME_ZONE_SCALE_MASK)
|
|
}
|
|
|
|
func isFloat(colType int, scale int) bool {
|
|
return colType == DECIMAL && scale == int(ORACLE_FLOAT_SCALE_MASK)
|
|
}
|
|
|
|
func getFloatPrec(prec int) int {
|
|
return int(math.Round(float64(prec)*0.30103)) + 1
|
|
}
|
|
|
|
func getFloatScale(scale int) int {
|
|
return scale & (^int(ORACLE_FLOAT_SCALE_MASK))
|
|
}
|
|
|
|
var (
|
|
scanTypeFloat32 = reflect.TypeOf(float32(0))
|
|
scanTypeFloat64 = reflect.TypeOf(float64(0))
|
|
scanTypeBool = reflect.TypeOf(false)
|
|
scanTypeInt8 = reflect.TypeOf(int8(0))
|
|
scanTypeInt16 = reflect.TypeOf(int16(0))
|
|
scanTypeInt32 = reflect.TypeOf(int32(0))
|
|
scanTypeInt64 = reflect.TypeOf(int64(0))
|
|
scanTypeNullBool = reflect.TypeOf(sql.NullBool{})
|
|
scanTypeNullFloat = reflect.TypeOf(sql.NullFloat64{})
|
|
scanTypeNullInt = reflect.TypeOf(sql.NullInt64{})
|
|
scanTypeNullString = reflect.TypeOf(sql.NullString{})
|
|
scanTypeNullTime = reflect.TypeOf(sql.NullTime{})
|
|
scanTypeRawBytes = reflect.TypeOf(sql.RawBytes{})
|
|
scanTypeString = reflect.TypeOf("")
|
|
scanTypeTime = reflect.TypeOf(time.Now())
|
|
scanTypeUnknown = reflect.TypeOf(new(interface{}))
|
|
)
|
|
|
|
func (column *column) ScanType() reflect.Type {
|
|
|
|
switch column.colType {
|
|
case BOOLEAN:
|
|
if column.nullable {
|
|
return scanTypeNullBool
|
|
}
|
|
|
|
return scanTypeBool
|
|
|
|
case BIT:
|
|
if strings.ToLower(column.typeName) == "boolean" {
|
|
|
|
if column.nullable {
|
|
return scanTypeNullBool
|
|
}
|
|
|
|
return scanTypeBool
|
|
} else {
|
|
|
|
if column.nullable {
|
|
return scanTypeNullInt
|
|
}
|
|
return scanTypeInt8
|
|
}
|
|
|
|
case TINYINT:
|
|
if column.nullable {
|
|
return scanTypeNullInt
|
|
}
|
|
return scanTypeInt8
|
|
|
|
case SMALLINT:
|
|
if column.nullable {
|
|
return scanTypeNullInt
|
|
}
|
|
return scanTypeInt16
|
|
|
|
case INT:
|
|
if column.nullable {
|
|
return scanTypeNullInt
|
|
}
|
|
|
|
return scanTypeInt32
|
|
|
|
case BIGINT:
|
|
if column.nullable {
|
|
return scanTypeNullInt
|
|
}
|
|
return scanTypeInt64
|
|
|
|
case REAL:
|
|
if column.nullable {
|
|
return scanTypeNullFloat
|
|
}
|
|
|
|
return scanTypeFloat32
|
|
|
|
case DOUBLE:
|
|
|
|
if strings.ToLower(column.typeName) == "float" {
|
|
if column.nullable {
|
|
return scanTypeNullFloat
|
|
}
|
|
|
|
return scanTypeFloat32
|
|
}
|
|
|
|
if column.nullable {
|
|
return scanTypeNullFloat
|
|
}
|
|
|
|
return scanTypeFloat64
|
|
case DATE, TIME, TIME_TZ, DATETIME, DATETIME_TZ, DATETIME2, DATETIME2_TZ:
|
|
if column.nullable {
|
|
return scanTypeNullTime
|
|
}
|
|
|
|
return scanTypeTime
|
|
|
|
case DECIMAL, BINARY, VARBINARY, BLOB:
|
|
return scanTypeRawBytes
|
|
|
|
case CHAR, VARCHAR2, VARCHAR, CLOB:
|
|
if column.nullable {
|
|
return scanTypeNullString
|
|
}
|
|
return scanTypeString
|
|
}
|
|
|
|
return scanTypeUnknown
|
|
}
|
|
|
|
func (column *column) Length() (length int64, ok bool) {
|
|
switch column.colType {
|
|
case BINARY, VARBINARY, BLOB, CHAR, VARCHAR2, VARCHAR, CLOB:
|
|
return int64(column.prec), true
|
|
}
|
|
|
|
return int64(0), false
|
|
}
|
|
|
|
func (column *column) PrecisionScale() (precision, scale int64, ok bool) {
|
|
switch column.colType {
|
|
case DECIMAL:
|
|
if column.prec == 0 {
|
|
return 38, int64(column.scale), true
|
|
} else {
|
|
return int64(column.prec), int64(column.scale), true
|
|
}
|
|
}
|
|
|
|
return int64(0), int64(0), false
|
|
}
|
|
|
|
func (column *column) getColumnData(bytes []byte, conn *DmConnection) (driver.Value, error) {
|
|
if bytes == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
switch column.colType {
|
|
case BOOLEAN:
|
|
return bytes[0] != 0, nil
|
|
case BIT:
|
|
if strings.ToLower(column.typeName) == "boolean" {
|
|
return bytes[0] != 0, nil
|
|
}
|
|
|
|
return int8(bytes[0]), nil
|
|
case TINYINT:
|
|
return int8(bytes[0]), nil
|
|
case SMALLINT:
|
|
return Dm_build_1346.Dm_build_1443(bytes, 0), nil
|
|
case INT:
|
|
return Dm_build_1346.Dm_build_1448(bytes, 0), nil
|
|
case BIGINT:
|
|
return Dm_build_1346.Dm_build_1453(bytes, 0), nil
|
|
case REAL:
|
|
return Dm_build_1346.Dm_build_1458(bytes, 0), nil
|
|
case DOUBLE:
|
|
|
|
return Dm_build_1346.Dm_build_1462(bytes, 0), nil
|
|
case DATE, TIME, DATETIME, TIME_TZ, DATETIME_TZ, DATETIME2, DATETIME2_TZ:
|
|
return DB2G.toTime(bytes, column, conn)
|
|
case INTERVAL_DT:
|
|
return newDmIntervalDTByBytes(bytes).String(), nil
|
|
case INTERVAL_YM:
|
|
return newDmIntervalYMByBytes(bytes).String(), nil
|
|
case DECIMAL:
|
|
tmp, err := DB2G.toDmDecimal(bytes, column, conn)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return tmp.String(), nil
|
|
|
|
case BINARY, VARBINARY:
|
|
return bytes, nil
|
|
case BLOB:
|
|
if isComplexType(int(column.colType), int(column.scale)) {
|
|
return DB2G.toComplexType(bytes, column, conn)
|
|
}
|
|
blob := DB2G.toDmBlob(bytes, column, conn)
|
|
|
|
l, err := blob.GetLength()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return blob.getBytes(1, int32(l))
|
|
|
|
case CHAR, VARCHAR2, VARCHAR:
|
|
return Dm_build_1346.Dm_build_1503(bytes, 0, len(bytes), conn.getServerEncoding(), conn), nil
|
|
case CLOB:
|
|
clob := DB2G.toDmClob(bytes, conn, column)
|
|
|
|
l, err := clob.GetLength()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
str, err := clob.getSubString(1, int32(l))
|
|
if err != nil {
|
|
return str, err
|
|
}
|
|
if conn.dmConnector.clobToBytes {
|
|
return []byte(str), err
|
|
} else {
|
|
return str, err
|
|
}
|
|
|
|
}
|
|
|
|
return string(bytes), nil
|
|
}
|
|
|
|
func emptyStringToNil(t int32) bool {
|
|
switch t {
|
|
case BOOLEAN, BIT, TINYINT, SMALLINT, INT, BIGINT, REAL, DOUBLE, DECIMAL, DATE, TIME,
|
|
DATETIME, INTERVAL_DT, INTERVAL_YM, TIME_TZ, DATETIME_TZ, DATETIME2, DATETIME2_TZ:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|