7f7b585ffb
- 在应用程序中新增对达梦数据库(DM)的配置和连接支持 - 实现 SetDmDB 函数以配置达梦数据库连接 - 更新数据库操作逻辑,支持达梦特有的 SQL 语法和功能 - 在相关文件中添加达梦数据库的处理逻辑,包括表创建、数据插入和查询 - 更新 go.mod 和 go.sum 文件以引入达梦数据库驱动 - 增强文档,详细说明达梦数据库的配置和使用方法
523 lines
11 KiB
Go
523 lines
11 KiB
Go
/*
|
|
* Copyright (c) 2000-2018, 达梦数据库有限公司.
|
|
* All rights reserved.
|
|
*/
|
|
package dm
|
|
|
|
import (
|
|
"database/sql/driver"
|
|
"io"
|
|
"reflect"
|
|
"strings"
|
|
)
|
|
|
|
type DmRows struct {
|
|
filterable
|
|
CurrentRows *innerRows
|
|
finish func()
|
|
}
|
|
|
|
func (r *DmRows) Columns() []string {
|
|
if err := r.CurrentRows.dmStmt.checkClosed(); err != nil {
|
|
return nil
|
|
}
|
|
if len(r.filterChain.filters) == 0 {
|
|
return r.columns()
|
|
}
|
|
return r.filterChain.reset().DmRowsColumns(r)
|
|
}
|
|
|
|
func (r *DmRows) Close() error {
|
|
if err := r.CurrentRows.dmStmt.checkClosed(); err != nil {
|
|
return err
|
|
}
|
|
if len(r.filterChain.filters) == 0 {
|
|
return r.close()
|
|
}
|
|
return r.filterChain.reset().DmRowsClose(r)
|
|
}
|
|
|
|
func (r *DmRows) Next(dest []driver.Value) error {
|
|
if err := r.CurrentRows.dmStmt.checkClosed(); err != nil {
|
|
return err
|
|
}
|
|
if len(r.filterChain.filters) == 0 {
|
|
return r.next(dest)
|
|
}
|
|
return r.filterChain.reset().DmRowsNext(r, dest)
|
|
}
|
|
|
|
func (r *DmRows) HasNextResultSet() bool {
|
|
if err := r.CurrentRows.dmStmt.checkClosed(); err != nil {
|
|
return false
|
|
}
|
|
if len(r.filterChain.filters) == 0 {
|
|
return r.hasNextResultSet()
|
|
}
|
|
return r.filterChain.reset().DmRowsHasNextResultSet(r)
|
|
}
|
|
|
|
func (r *DmRows) NextResultSet() error {
|
|
if err := r.CurrentRows.dmStmt.checkClosed(); err != nil {
|
|
return err
|
|
}
|
|
if len(r.filterChain.filters) == 0 {
|
|
return r.nextResultSet()
|
|
}
|
|
return r.filterChain.reset().DmRowsNextResultSet(r)
|
|
}
|
|
|
|
func (r *DmRows) ColumnTypeScanType(index int) reflect.Type {
|
|
if err := r.CurrentRows.dmStmt.checkClosed(); err != nil {
|
|
return nil
|
|
}
|
|
if len(r.filterChain.filters) == 0 {
|
|
return r.columnTypeScanType(index)
|
|
}
|
|
return r.filterChain.reset().DmRowsColumnTypeScanType(r, index)
|
|
}
|
|
|
|
func (r *DmRows) ColumnTypeDatabaseTypeName(index int) string {
|
|
if err := r.CurrentRows.dmStmt.checkClosed(); err != nil {
|
|
return ""
|
|
}
|
|
if len(r.filterChain.filters) == 0 {
|
|
return r.columnTypeDatabaseTypeName(index)
|
|
}
|
|
return r.filterChain.reset().DmRowsColumnTypeDatabaseTypeName(r, index)
|
|
}
|
|
|
|
func (r *DmRows) ColumnTypeLength(index int) (length int64, ok bool) {
|
|
if err := r.CurrentRows.dmStmt.checkClosed(); err != nil {
|
|
return -1, false
|
|
}
|
|
if len(r.filterChain.filters) == 0 {
|
|
return r.columnTypeLength(index)
|
|
}
|
|
return r.filterChain.reset().DmRowsColumnTypeLength(r, index)
|
|
}
|
|
|
|
func (r *DmRows) ColumnTypeNullable(index int) (nullable, ok bool) {
|
|
if err := r.CurrentRows.dmStmt.checkClosed(); err != nil {
|
|
return false, false
|
|
}
|
|
if len(r.filterChain.filters) == 0 {
|
|
return r.columnTypeNullable(index)
|
|
}
|
|
return r.filterChain.reset().DmRowsColumnTypeNullable(r, index)
|
|
}
|
|
|
|
func (r *DmRows) ColumnTypePrecisionScale(index int) (precision, scale int64, ok bool) {
|
|
if err := r.CurrentRows.dmStmt.checkClosed(); err != nil {
|
|
return -1, -1, false
|
|
}
|
|
if len(r.filterChain.filters) == 0 {
|
|
return r.columnTypePrecisionScale(index)
|
|
}
|
|
return r.filterChain.reset().DmRowsColumnTypePrecisionScale(r, index)
|
|
}
|
|
|
|
func (dest *DmRows) Scan(src interface{}) error {
|
|
switch src := src.(type) {
|
|
case nil:
|
|
*dest = *new(DmRows)
|
|
return nil
|
|
case *DmRows:
|
|
*dest = *src
|
|
return nil
|
|
default:
|
|
return UNSUPPORTED_SCAN
|
|
}
|
|
}
|
|
|
|
func (rows *DmRows) columns() []string {
|
|
return rows.CurrentRows.Columns()
|
|
}
|
|
|
|
func (rows *DmRows) close() error {
|
|
if f := rows.finish; f != nil {
|
|
f()
|
|
rows.finish = nil
|
|
}
|
|
return rows.CurrentRows.Close()
|
|
}
|
|
|
|
func (rows *DmRows) next(dest []driver.Value) error {
|
|
return rows.CurrentRows.Next(dest)
|
|
}
|
|
|
|
func (rows *DmRows) hasNextResultSet() bool {
|
|
return rows.CurrentRows.HasNextResultSet()
|
|
}
|
|
|
|
func (rows *DmRows) nextResultSet() error {
|
|
return rows.CurrentRows.NextResultSet()
|
|
}
|
|
|
|
func (rows *DmRows) columnTypeScanType(index int) reflect.Type {
|
|
return rows.CurrentRows.ColumnTypeScanType(index)
|
|
}
|
|
|
|
func (rows *DmRows) columnTypeDatabaseTypeName(index int) string {
|
|
return rows.CurrentRows.ColumnTypeDatabaseTypeName(index)
|
|
}
|
|
|
|
func (rows *DmRows) columnTypeLength(index int) (length int64, ok bool) {
|
|
return rows.CurrentRows.ColumnTypeLength(index)
|
|
}
|
|
|
|
func (rows *DmRows) columnTypeNullable(index int) (nullable, ok bool) {
|
|
return rows.CurrentRows.ColumnTypeNullable(index)
|
|
}
|
|
|
|
func (rows *DmRows) columnTypePrecisionScale(index int) (precision, scale int64, ok bool) {
|
|
return rows.CurrentRows.ColumnTypePrecisionScale(index)
|
|
}
|
|
|
|
type innerRows struct {
|
|
dmStmt *DmStatement
|
|
|
|
id int16
|
|
|
|
columns []column
|
|
|
|
datas [][][]byte
|
|
|
|
datasOffset int
|
|
|
|
datasStartPos int64
|
|
|
|
currentPos int64
|
|
|
|
totalRowCount int64
|
|
|
|
fetchSize int
|
|
|
|
sizeOfRow int
|
|
|
|
isBdta bool
|
|
|
|
nextExecInfo *execRetInfo
|
|
|
|
next *innerRows
|
|
|
|
dmRows *DmRows
|
|
|
|
closed bool
|
|
}
|
|
|
|
func (innerRows *innerRows) checkClosed() error {
|
|
if innerRows.closed {
|
|
return ECGO_RESULTSET_CLOSED.throw()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (innerRows *innerRows) Columns() []string {
|
|
if err := innerRows.checkClosed(); err != nil {
|
|
return nil
|
|
}
|
|
|
|
columnNames := make([]string, len(innerRows.columns))
|
|
nameCase := innerRows.dmStmt.dmConn.dmConnector.columnNameCase
|
|
|
|
for i, column := range innerRows.columns {
|
|
if nameCase == COLUMN_NAME_NATURAL_CASE {
|
|
columnNames[i] = column.name
|
|
} else if nameCase == COLUMN_NAME_UPPER_CASE {
|
|
columnNames[i] = strings.ToUpper(column.name)
|
|
} else if nameCase == COLUMN_NAME_LOWER_CASE {
|
|
columnNames[i] = strings.ToLower(column.name)
|
|
} else {
|
|
columnNames[i] = column.name
|
|
}
|
|
}
|
|
|
|
return columnNames
|
|
}
|
|
|
|
func (innerRows *innerRows) Close() error {
|
|
if innerRows.closed {
|
|
return nil
|
|
}
|
|
|
|
innerRows.closed = true
|
|
|
|
if innerRows.dmStmt.innerUsed {
|
|
innerRows.dmStmt.close()
|
|
} else {
|
|
delete(innerRows.dmStmt.rsMap, innerRows.id)
|
|
}
|
|
|
|
innerRows.dmStmt = nil
|
|
|
|
return nil
|
|
}
|
|
|
|
func (innerRows *innerRows) Next(dest []driver.Value) error {
|
|
err := innerRows.checkClosed()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if innerRows.totalRowCount == 0 || innerRows.currentPos >= innerRows.totalRowCount {
|
|
return io.EOF
|
|
}
|
|
|
|
if innerRows.currentPos+1 == innerRows.totalRowCount {
|
|
innerRows.currentPos++
|
|
innerRows.datasOffset++
|
|
return io.EOF
|
|
}
|
|
|
|
if innerRows.currentPos+1 < innerRows.datasStartPos || innerRows.currentPos+1 >= innerRows.datasStartPos+int64(len(innerRows.datas)) {
|
|
if innerRows.fetchData(innerRows.currentPos + 1) {
|
|
innerRows.currentPos++
|
|
err := innerRows.getRowData(dest)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
innerRows.currentPos++
|
|
innerRows.datasOffset++
|
|
return io.EOF
|
|
}
|
|
} else {
|
|
innerRows.currentPos++
|
|
innerRows.datasOffset++
|
|
err := innerRows.getRowData(dest)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (innerRows *innerRows) HasNextResultSet() bool {
|
|
err := innerRows.checkClosed()
|
|
if err != nil {
|
|
return false
|
|
}
|
|
|
|
if innerRows.nextExecInfo != nil {
|
|
return innerRows.nextExecInfo.hasResultSet
|
|
}
|
|
|
|
innerRows.nextExecInfo, err = innerRows.dmStmt.dmConn.Access.Dm_build_543(innerRows.dmStmt, 0)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
|
|
if innerRows.nextExecInfo.hasResultSet {
|
|
innerRows.next = newInnerRows(innerRows.id+1, innerRows.dmStmt, innerRows.nextExecInfo)
|
|
return true
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func (innerRows *innerRows) NextResultSet() error {
|
|
err := innerRows.checkClosed()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if innerRows.nextExecInfo == nil {
|
|
innerRows.HasNextResultSet()
|
|
}
|
|
|
|
if innerRows.next == nil {
|
|
return io.EOF
|
|
}
|
|
|
|
innerRows.next.dmRows = innerRows.dmRows
|
|
innerRows.dmRows.CurrentRows = innerRows.next
|
|
return nil
|
|
}
|
|
|
|
func (innerRows *innerRows) ColumnTypeScanType(index int) reflect.Type {
|
|
if err := innerRows.checkClosed(); err != nil {
|
|
return nil
|
|
}
|
|
if column := innerRows.checkIndex(index); column != nil {
|
|
return column.ScanType()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (innerRows *innerRows) ColumnTypeDatabaseTypeName(index int) string {
|
|
if err := innerRows.checkClosed(); err != nil {
|
|
return ""
|
|
}
|
|
if column := innerRows.checkIndex(index); column != nil {
|
|
return column.typeName
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func (innerRows *innerRows) ColumnTypeLength(index int) (length int64, ok bool) {
|
|
if err := innerRows.checkClosed(); err != nil {
|
|
return 0, false
|
|
}
|
|
if column := innerRows.checkIndex(index); column != nil {
|
|
return column.Length()
|
|
}
|
|
return 0, false
|
|
}
|
|
|
|
func (innerRows *innerRows) ColumnTypeNullable(index int) (nullable, ok bool) {
|
|
if err := innerRows.checkClosed(); err != nil {
|
|
return false, false
|
|
}
|
|
if column := innerRows.checkIndex(index); column != nil {
|
|
return column.nullable, true
|
|
}
|
|
return false, false
|
|
}
|
|
|
|
func (innerRows *innerRows) ColumnTypePrecisionScale(index int) (precision, scale int64, ok bool) {
|
|
if err := innerRows.checkClosed(); err != nil {
|
|
return 0, 0, false
|
|
}
|
|
if column := innerRows.checkIndex(index); column != nil {
|
|
return column.PrecisionScale()
|
|
}
|
|
return 0, 0, false
|
|
}
|
|
|
|
func newDmRows(currentRows *innerRows) *DmRows {
|
|
dr := new(DmRows)
|
|
dr.resetFilterable(¤tRows.dmStmt.filterable)
|
|
dr.CurrentRows = currentRows
|
|
dr.idGenerator = dmRowsIDGenerator
|
|
currentRows.dmRows = dr
|
|
return dr
|
|
}
|
|
|
|
func newInnerRows(id int16, stmt *DmStatement, execInfo *execRetInfo) *innerRows {
|
|
rows := new(innerRows)
|
|
rows.id = id
|
|
rows.dmStmt = stmt
|
|
rows.columns = stmt.columns
|
|
rows.datas = execInfo.rsDatas
|
|
rows.totalRowCount = execInfo.updateCount
|
|
rows.isBdta = execInfo.rsBdta
|
|
rows.fetchSize = stmt.fetchSize
|
|
|
|
if len(execInfo.rsDatas) == 0 {
|
|
rows.sizeOfRow = 0
|
|
} else {
|
|
rows.sizeOfRow = execInfo.rsSizeof / len(execInfo.rsDatas)
|
|
}
|
|
|
|
rows.currentPos = -1
|
|
rows.datasOffset = -1
|
|
rows.datasStartPos = 0
|
|
|
|
rows.nextExecInfo = nil
|
|
rows.next = nil
|
|
|
|
if rows.dmStmt.rsMap != nil {
|
|
rows.dmStmt.rsMap[rows.id] = rows
|
|
}
|
|
|
|
if stmt.dmConn.dmConnector.enRsCache && execInfo.rsCacheOffset > 0 &&
|
|
int64(len(execInfo.rsDatas)) == execInfo.updateCount {
|
|
rp.put(stmt, stmt.nativeSql, execInfo)
|
|
}
|
|
|
|
return rows
|
|
}
|
|
|
|
func newLocalInnerRows(stmt *DmStatement, columns []column, rsDatas [][][]byte) *innerRows {
|
|
rows := new(innerRows)
|
|
rows.id = 0
|
|
rows.dmStmt = stmt
|
|
rows.fetchSize = stmt.fetchSize
|
|
|
|
if columns == nil {
|
|
rows.columns = make([]column, 0)
|
|
} else {
|
|
rows.columns = columns
|
|
}
|
|
|
|
if rsDatas == nil {
|
|
rows.datas = make([][][]byte, 0)
|
|
rows.totalRowCount = 0
|
|
} else {
|
|
rows.datas = rsDatas
|
|
rows.totalRowCount = int64(len(rsDatas))
|
|
}
|
|
|
|
rows.isBdta = false
|
|
return rows
|
|
}
|
|
|
|
func (innerRows *innerRows) checkIndex(index int) *column {
|
|
if index < 0 || index > len(innerRows.columns)-1 {
|
|
return nil
|
|
}
|
|
|
|
return &innerRows.columns[index]
|
|
}
|
|
|
|
func (innerRows *innerRows) fetchData(startPos int64) bool {
|
|
execInfo, err := innerRows.dmStmt.dmConn.Access.Dm_build_550(innerRows, startPos)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
|
|
innerRows.totalRowCount = execInfo.updateCount
|
|
if execInfo.rsDatas != nil {
|
|
innerRows.datas = execInfo.rsDatas
|
|
innerRows.datasStartPos = startPos
|
|
innerRows.datasOffset = 0
|
|
return true
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func (innerRows *innerRows) getRowData(dest []driver.Value) (err error) {
|
|
for i, column := range innerRows.columns {
|
|
|
|
if i <= len(dest)-1 {
|
|
if column.colType == CURSOR {
|
|
var tmpExecInfo *execRetInfo
|
|
tmpExecInfo, err = innerRows.dmStmt.dmConn.Access.Dm_build_543(innerRows.dmStmt, 1)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if tmpExecInfo.hasResultSet {
|
|
dest[i] = newDmRows(newInnerRows(innerRows.id+1, innerRows.dmStmt, tmpExecInfo))
|
|
} else {
|
|
dest[i] = nil
|
|
}
|
|
continue
|
|
}
|
|
|
|
dest[i], err = column.getColumnData(innerRows.datas[innerRows.datasOffset][i+1], innerRows.dmStmt.dmConn)
|
|
innerRows.columns[i].isBdta = innerRows.isBdta
|
|
if err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (innerRows *innerRows) getRowCount() int64 {
|
|
innerRows.checkClosed()
|
|
|
|
if innerRows.totalRowCount == INT64_MAX {
|
|
return -1
|
|
}
|
|
|
|
return innerRows.totalRowCount
|
|
}
|