GTong před 1 rokem
revize
109d6cbb06

+ 3 - 0
go.mod

@@ -0,0 +1,3 @@
+module xugu_driver
+
+go 1.22.2

+ 50 - 0
main.go

@@ -0,0 +1,50 @@
+package main
+
+import (
+	"database/sql"
+	"log"
+	_ "xugu_driver/xugu"
+)
+
+func main() {
+	db, err := sql.Open("xugusql", "IP=10.28.20.101;DB=SYSTEM;User=SYSDBA;PWD=SYSDBA;Port=5190;AUTO_COMMIT=on;CHAR_SET=UTF8")
+	//_, err = db.Exec("create table go_test(c1 int, c2 varchar);")
+	db.Ping()
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	// row, err := db.Query("select * from gotest2;")
+	// if err != nil {
+	// 	fmt.Println("查询错误", err)
+	// }
+	// fmt.Println("row", row)
+
+	// col, _ := row.Columns()
+	// fmt.Println("row.Columns(): ", col)
+
+	// type1, _ := row.ColumnTypes()
+
+	// fmt.Printf("type:%#v \n ", type1)
+
+	// pvals := make([]interface{}, len(col))
+	// for row.Next() {
+	// 	fmt.Println("row.Next():main ")
+	// 	row.Scan(pvals)
+	// 	for _, v := range pvals {
+	// 		fmt.Printf("pvals:%v\t \n", v)
+
+	// 		fmt.Printf("pvals:%v\t \n", (v.(*[]byte)))
+	// 	}
+
+	// 	fmt.Printf("\n")
+	// 	break
+	// }
+
+	//消息类型为E
+	//db.Exec("drop table ta3;")
+
+	db.Exec("INSERT INTO  gotest VALUES('gc');")
+	db.Close()
+
+}

+ 82 - 0
select A.ini

@@ -0,0 +1,82 @@
+
+SelectResult: 
+    A
+Field_Num:   结果集字段数
+- \x00\x00\x00\x01\
+Field_Name_Len:  字段名长度(网络序32位整数)
+- x00\x00\x00\v
+Field_Name:  字段名(字节数与Field_Name_Len一致)
+- GOTEST.NAME 
+
+Field_DType:     字段数据类型ID(网络序32位整数)
+\x00\x00\x00\x1e 
+
+Field_Preci_Scale:字段的精度标度(网络序32位整数,若为-1或0表示没有精度标度信息,否则高16位为精度低16位为标度)
+\xff\xff\xff\xff 
+Field_Flag: 字段的其它属性标志(网络序32位整数),其中每一个位表征字段的某一个特性,各位对应的特性如下
+X04 x01
+     0000 0100 0000 0001
+            FIELD_BASE_TAB=1,   /* 表示字段是实际的表的字段(否则,表示字段实际上是表达式) */
+            FIELD_NOT_NULL=2,   /* 表示字段具有非空约束 */
+            FIELD_IS_PRIMARY=4,     /* 表示字段是主键 */
+            FIELD_IS_SERIAL=8,  /* 表示字段是序列值 */
+            FIELD_IS_TIMESTAMP=16,  /* 表示字段是timestamp */
+            FIELD_IS_LOB=32,    /* 表示字段是大对象类型 */
+            FIELD_IS_UNIQUE=64, /* 表示字段是唯一值类型 */
+            FIELD_IS_ROWID=128, /* 表示字段是rowid */
+            FIELD_IS_DUMMY=256,     /* 表示字段不应输出 */
+            FIELD_IS_HIDE=512,  /* 表示字段是隐藏字段 */
+\x00\x00\x04\x01  1025
+
+        
+R
+Col_len:     列数据长度(网络序32位整数)
+\x00\x00\x00\x02 
+Col_Data:    列数据(字节数与Col_Len一致)
+gt
+R
+Col_len:     列数据长度(网络序32位整数)
+\x00\x00\x00\x02
+Col_Data:    列数据(字节数与Col_Len一致)
+gcK
+
+
+
+A
+\x00\x00\x00\x02 Field_Num:   结果集字段数
+
+\x00\x00\x00\n Field_Name_Len:  字段名长度(网络序32位整数)
+GOTEST2.ID  字段名(字节数与Field_Name_Len一致)
+\x00\x00\x00\x05    Field_DType:     字段数据类型ID(网络序32位整数)
+\xff\xff\xff\xff    Field_Preci_Scale:字段的精度标度
+\x00\x00\x00\x01    Field_Flag: 字段的其它属性标志
+
+\x00\x00\x00\f  Field_Name_Len:  字段名长度(网络序32位整数)
+GOTEST2.NAME    字段名(字节数与Field_Name_Len一致)
+\x00\x00\x00\x1e    Field_DType:     字段数据类型ID(网络序32位整数)
+\xff\xff\xff\xff    ield_Preci_Scale:字段的精度标度
+\x00\x00\x04\x01    Field_Flag: 字段的其它属性标志
+
+R
+\x00\x00\x00\x04
+\x00\x00\x00\x01
+
+\x00\x00\x00\x02
+gc
+
+R
+\x00\x00\x00\x04
+\x00\x00\x00\x02
+
+\x00\x00\x00\x02
+gc
+
+R
+\x00\x00\x00\x04
+\x00\x00\x00\x03
+
+\x00\x00\x00\x02
+gc
+
+K
+

+ 2 - 0
todo.txt

@@ -0,0 +1,2 @@
+//tcp接收数据库消息方式需要重写
+//获取字段类型 需要重写

+ 62 - 0
xugu/buffer.go

@@ -0,0 +1,62 @@
+package xugu
+
+import (
+	"errors"
+	"fmt"
+	"net"
+	"time"
+)
+
+var (
+	ErrBusyBuffer = errors.New("busy buffer")
+)
+
+type buffer struct {
+	buf     []byte // buf 是一个字节缓冲区,长度和容量相等。
+	conn    net.Conn
+	idx     int
+	length  int
+	timeout time.Duration
+}
+
+// newBuffer 分配并返回一个新的缓冲区。
+func newBuffer(nc net.Conn) buffer {
+	return buffer{
+		buf:  make([]byte, 2048),
+		conn: nc,
+	}
+}
+
+// store 存储 buf,这是一个更新的缓冲区,如果合适的话。
+func (b *buffer) store(buf []byte) error {
+	if len(buf) > cap(b.buf) {
+		fmt.Println("大于缓冲区: len(buf) > cap(b.buf)")
+		return ErrBusyBuffer
+	}
+	b.buf = buf[:cap(buf)]
+
+	return nil
+}
+
+func (b *buffer) peekChar() byte {
+	ret := b.buf[b.idx]
+	return ret
+}
+
+// 返回指定字节的切片
+func (b *buffer) readNext(need int) []byte {
+	//长度不够返回
+	if len(b.buf[b.idx:]) < need {
+		//调用一次接收服务器消息
+		b.conn.Read(b.buf[b.idx:])
+		offset := b.idx
+		b.idx += need
+
+		return b.buf[offset:b.idx]
+	}
+
+	offset := b.idx
+	b.idx += need
+	//fmt.Println("readNext: ", b.buf[offset:b.idx])
+	return b.buf[offset:b.idx]
+}

+ 96 - 0
xugu/xugu_conn.go

@@ -0,0 +1,96 @@
+package xugu
+
+import (
+	"bytes"
+	"database/sql/driver"
+	"errors"
+	"fmt"
+	"net"
+	"sync"
+)
+
+type xuguConn struct {
+	dsnConfig
+	conn        net.Conn
+	mu          sync.Mutex
+	Type        HANDLE_TYPE // 句柄类型
+	useSSL      bool        // 是否使用加密
+	havePrepare int         //default 0
+
+	bkChar      byte
+	prepareNo   int //fashengqi
+	prepareName string
+	//presPrepareCata *Result
+	params *XGCSParam
+	errStr []byte
+
+	sendBuff bytes.Buffer
+	readBuff buffer
+}
+
+type dsnConfig struct {
+	IP             string
+	Port           string
+	Database       string
+	User           string
+	Password       string
+	Encryptor      string //加密库的解密口令
+	CharSet        string //客户端使用的字符集名
+	TimeZone       string
+	IsoLevel       string //事务隔离级别
+	LockTimeout    string //加锁超时
+	AutoCommit     string
+	StrictCommit   string
+	Result         string
+	ReturnSchema   string
+	ReturnCursorID string
+	LobRet         string
+	ReturnRowid    string
+	Version        string
+}
+
+func (xgConn *xuguConn) get_error() error {
+
+	return nil
+}
+
+func (xgConn *xuguConn) Begin() (driver.Tx, error) {
+
+	return nil, nil
+
+}
+
+func (xgConn *xuguConn) Prepare(query string) (driver.Stmt, error) {
+	//判断sql类型
+	switch switchSQLType(query) {
+	case SQL_PROCEDURE:
+		return nil, errors.New("Prepare does not support stored procedures")
+	case SQL_UNKNOWN:
+		return nil, errors.New("Unknown SQL statement type")
+	case SQL_CREATE:
+		return nil, errors.New("Prepare does not support DDL.")
+	}
+
+	stmt := &xuguStmt{
+		stmt_conn:   xgConn,
+		prepared:    true,
+		prename:     make([]byte, 128),
+		curopend:    false,
+		curname:     make([]byte, 128),
+		param_count: 0,
+		result:      nil,
+		mysql:       query,
+	}
+
+	return stmt, nil
+}
+
+func (xgConn *xuguConn) Close() error {
+	fmt.Println("Close connection")
+	err := xgConn.conn.Close()
+	if err != nil {
+		fmt.Println("Close connection error")
+		return err
+	}
+	return nil
+}

+ 62 - 0
xugu/xugu_connector.go

@@ -0,0 +1,62 @@
+package xugu
+
+import (
+	"context"
+	"database/sql/driver"
+	"fmt"
+	"net"
+	"time"
+)
+
+type connector struct {
+	dsn string
+}
+
+// Driver implements driver.Connector interface.
+// Driver returns &XuguDriver{}
+func (conntor *connector) Driver() driver.Driver {
+	return &XuguDriver{}
+}
+
+// Connect implements driver.Connector interface.
+// Connect returns a connection to the database.
+/*
+dsn解析
+创建连接
+设置为 tcp 长连接(
+创建连接缓冲区
+设置连接超时配置
+接收来自服务端的握手请求
+*/
+func (conntor *connector) Connect(ctx context.Context) (driver.Conn, error) {
+	dsnConfig := parseDSN(conntor.dsn)
+
+	xgConn := &xuguConn{conn: nil}
+	xgConn.dsnConfig = dsnConfig
+
+	nd := net.Dialer{Timeout: 5 * time.Second}
+	netConn, err := nd.DialContext(ctx, "tcp", fmt.Sprintf("%s:%s", xgConn.IP, xgConn.Port))
+	if err != nil {
+		fmt.Println("tcp error", err)
+		return nil, err
+	}
+
+	// 启用 TCP 保活
+	if tc, ok := netConn.(*net.TCPConn); ok {
+		if err := tc.SetKeepAlive(true); err != nil {
+			//c.cfg.Logger.Print(err) // 如果设置保活失败,记录错误但不终止
+			fmt.Println("SetKeepAlive error", err)
+		}
+	}
+
+	xgConn.conn = netConn
+	xgConn.Type = HT_CONN
+	xgConn.readBuff = newBuffer(xgConn.conn)
+	fmt.Println("连接串为: ", conntor.dsn)
+	re, _ := xgSockOpenConn(ctx, xgConn)
+	if re < 0 {
+		return nil, xgConn.get_error()
+	}
+
+	return xgConn, nil
+}

+ 164 - 0
xugu/xugu_define.go

@@ -0,0 +1,164 @@
+package xugu
+
+// 定义 HANDLE_TYPE 枚举
+type HANDLE_TYPE int
+
+const (
+	HT_UNKNOW   HANDLE_TYPE = iota // 0 未知
+	HT_CONN                        // 1 连接
+	HT_PARAMS                      // 2 参数
+	HT_ERR                         // 错误
+	HT_RS                          // 结果
+	HT_LOB                         // 大对象
+	HT_SMART_RS                    // 智能结果集
+)
+const (
+	XG_OK = iota
+)
+
+const (
+	XG_ERROR        = -1
+	XG_NET_ERROR    = -4
+	XG_INVALID_ARG  = -3
+	XG_SOCKET_ERROR = -8
+	XG_LOGIN_ERROR  = -9
+)
+
+const (
+	ERROR_BUFF_SIZE        uint = 1024
+	PREPARE_NAME_BUFF_SIZE uint = 128
+	CURSOR_NAME_BUFF_SIZE  uint = 128
+	ROWID_BUFF_SIZE        uint = 256
+	COLUMN_NAME_BUFF_SIZE  uint = 256
+
+	FIELD_BUFF_SIZE uint = 4096
+	LOB_BUFF_SIZE   uint = 8
+	RET_NO_DATA     int  = 100
+
+	// SQL_UNKNOWN   int = 0
+	// SQL_SELECT    int = 4
+	// SQL_CREATE    int = 5
+	// SQL_PROCEDURE int = 10
+
+	SQL_PARAM_INPUT       int = 1
+	SQL_PARAM_OUTPUT      int = 2
+	SQL_PARAM_INPUTOUTPUT int = 3
+	SQL_PARAM_RETURNVALUE int = 6
+
+	SQL_XG_C_CHAR int = 2
+	SQL_XG_C_CLOB int = 41
+	SQL_XG_C_BLOB int = 42
+	SQL_XG_C_NULL int = -11
+
+	BIND_PARAM_BY_NAME int = 62
+	BIND_PARAM_BY_POS  int = 63
+)
+
+// SQL类型常量
+const (
+	SQL_UNKNOWN = iota
+	SQL_SELECT
+	SQL_INSERT
+	SQL_UPDATE
+	SQL_DELETE
+	SQL_CREATE
+	SQL_ALTER
+	SQL_PROCEDURE
+	SQL_OTHER
+)
+
+const (
+	TYPE_EMPTY        = 0
+	TYPE_NULL         = 1
+	TYPE_BOOL         = 2
+	TYPE_I1           = 3
+	TYPE_I2           = 4
+	TYPE_I4           = 5
+	TYPE_I8           = 6
+	TYPE_NUM          = 7
+	TYPE_R4           = 8
+	TYPE_R8           = 9
+	TYPE_DATE         = 10
+	TYPE_TIME         = 11
+	TYPE_TIME_TZ      = 12
+	TYPE_DATETIME     = 13
+	TYPE_DATETIME_TZ  = 14
+	TYPE_INTERVAL_Y   = 15
+	TYPE_INTERVAL_Y2M = 16
+	TYPE_INTERVAL_M   = 17
+	TYPE_INTERVAL_D   = 18
+	TYPE_INTERVAL_D2H = 19
+	TYPE_INTERVAL_H   = 20
+	TYPE_INTERVAL_D2M = 21
+	TYPE_INTERVAL_H2M = 22
+	TYPE_INTERVAL_MI  = 23
+	TYPE_INTERVAL_D2S = 24
+	TYPE_INTERVAL_H2S = 25
+	TYPE_INTERVAL_M2S = 26
+	TYPE_INTERVAL_S   = 27
+	TYPE_ROWVER       = 28
+	TYPE_GUID         = 29
+	TYPE_CHAR         = 30
+	TYPE_NCHAR        = 31
+	TYPE_CLOB         = 32
+	TYPE_BINARY       = 33
+	TYPE_BLOB         = 34
+	TYPE_GEOM         = 35
+	TYPE_POINT        = 36
+	TYPE_BOX          = 37
+	TYPE_POLYLINE     = 38
+	TYPE_POLYGON      = 39
+	TYPE_BLOB_I       = 40
+	TYPE_BLOB_S       = 41
+	TYPE_BLOB_M       = 42
+	TYPE_BLOB_OM      = 43
+	TYPE_STREAM       = 44
+	TYPE_ROWID        = 45
+	TYPE_SIBLING      = 46
+	TYPE_MAX_SYS      = 47
+	TYPE_BLADE_BEGIN  = 101
+	TYPE_BLADE_END    = 1000
+	TYPE_OBJECT       = 1001
+	TYPE_REFROW       = 1002
+	TYPE_RECORD       = 1003
+	TYPE_VARRAY       = 1004
+	TYPE_TABLE        = 1005
+	TYPE_ITABLE       = 1006
+	TYPE_CURSOR       = 1007
+	TYPE_REFCUR       = 1008
+	TYPE_ROWTYPE      = 1009
+	TYPE_COLTYPE      = 1010
+	TYPE_CUR_REC      = 1011
+	TYPE_PARAM        = 1012
+)
+
+const (
+	XG_C_NULL                   = 0
+	XG_C_BOOL                   = 1
+	XG_C_CHAR                   = 2
+	XG_C_TINYINT                = 3
+	XG_C_SHORT                  = 4
+	XG_C_INTEGER                = 5
+	XG_C_BIGINT                 = 6
+	XG_C_FLOAT                  = 7
+	XG_C_DOUBLE                 = 8
+	XG_C_NUMERIC                = 9
+	XG_C_DATE                   = 10
+	XG_C_TIME                   = 11
+	XG_C_TIME_TZ                = 12
+	XG_C_DATETIME               = 13
+	XG_C_DATETIME_TZ            = 14
+	XG_C_BINARY                 = 15
+	XG_C_INTERVAL               = 21
+	XG_C_INTERVAL_YEAR_TO_MONTH = 28
+	XG_C_INTERVAL_DAY_TO_SECOND = 31
+	XG_C_CLOB                   = 41
+	XG_C_BLOB                   = 42
+)
+
+const XG_C_NCHAR = 162 /* only for c# wchar use */
+const XG_C_CHARN1 = 163
+const DATETIME_ASLONG = 123
+
+const XG_PARAM_NAME_ERR = -53
+const XG_BIND_PARAM_ERROR = -59

+ 51 - 0
xugu/xugu_driver.go

@@ -0,0 +1,51 @@
+package xugu
+
+import (
+	"context"
+	"database/sql"
+	"database/sql/driver"
+	"fmt"
+	"time"
+)
+
+// XuguDriver is exported to make the driver directly accessible
+type XuguDriver struct{}
+
+/* Register Driver */
+func init() {
+	fmt.Println(">>>>>init()")
+	/* Register makes a database driver available by the provided name.
+	 * If Register is called twice with the same name or if driver is nil,
+	 * it panics.
+	 */
+	sql.Register("xugusql", &XuguDriver{})
+	timezone, _ := time.LoadLocation("Asia/Shanghai")
+	time.Local = timezone
+}
+
+// Open opens a database specified by its database driver name and a
+// driver-specific data source name, usually consisting of at least a
+// database name and connection information.
+//
+// Most users will open a database via a driver-specific connection
+// helper function that returns a *DB. No database drivers are included
+// in the Go standard library. See https://golang.org/s/sqldrivers for
+// a list of third-party drivers.
+//
+// Open may just validate its arguments without creating a connection
+// to the database. To verify that the data source name is valid, call
+// Ping.
+// The returned DB is safe for concurrent use by multiple goroutines
+// and maintains its own pool of idle connections. Thus, the Open
+// function should be called just once. It is rarely necessary to
+// close a DB.
+func (db XuguDriver) Open(dsn string) (driver.Conn, error) {
+	fmt.Println(">>>>>(db XuguDriver) Open")
+	conn := &connector{dsn: dsn}
+	return conn.Connect(context.Background())
+}
+
+func (db XuguDriver) OpenConnector(dsn string) (driver.Connector, error) {
+	fmt.Println(">>>>>(db XuguDriver) OpenConnector")
+	return &connector{dsn: dsn}, nil
+}

+ 160 - 0
xugu/xugu_fields.go

@@ -0,0 +1,160 @@
+package xugu
+
+import (
+	"database/sql"
+	"reflect"
+	"time"
+)
+
+type xuguField struct {
+	tableName string
+
+	/*
+	 * Store the name of the column
+	 * name of the current field
+	 * */
+	name   string
+	length int
+
+	/*
+	 * Store the data type information
+	 * of the current field column
+	 * */
+	fieldType fieldType
+}
+
+type fieldType byte
+
+const (
+	fieldTypeBool fieldType = iota + 0x01
+	fieldTypeChar
+	fieldTypeTinyint
+	fieldTypeShort
+	fieldTypeInteger
+	fieldTypeBigint
+	fieldTypeFloat
+	fieldTypeDouble
+	fieldTypeNumeric
+	fieldTypeDate
+	fieldTypeTime
+	fieldTypeTimeTZ
+	fieldTypeDatetime   fieldType = 23
+	fieldTypeDatetimeTZ fieldType = 14
+	fieldTypeBinary     fieldType = 15
+	fieldTypeVarChar    fieldType = 30
+
+	fieldTypeInterval    fieldType = 21
+	fieldTypeIntervalY2M fieldType = 28
+	fieldTypeIntervalD2S fieldType = 31
+	fieldTypeLob         fieldType = 40
+	fieldTypeClob        fieldType = 41
+	fieldTypeBlob        fieldType = 42
+)
+
+/* {{ */
+func (self *FieldDescri) typeDatabaseName() string {
+	switch self.FieldType {
+	case fieldTypeBool:
+		return "BOOLEAN"
+	case fieldTypeChar, fieldTypeVarChar:
+		return "CHAR"
+	case fieldTypeTinyint:
+		return "TINYINT"
+	case fieldTypeShort:
+		return "SHORT"
+	case fieldTypeInteger:
+		return "INTEGER"
+	case fieldTypeBigint:
+		return "BIGINT"
+	case fieldTypeFloat:
+		return "FLOAT"
+	case fieldTypeDouble:
+		return "DOUBLE"
+	case fieldTypeNumeric:
+		return "NUMERIC"
+	case fieldTypeDate:
+		return "DATE"
+	case fieldTypeTime:
+		return "TIME"
+	case fieldTypeTimeTZ:
+		return "TIMEZONE"
+	case fieldTypeDatetime:
+		return "DATETIME"
+	case fieldTypeDatetimeTZ:
+		return "DATETIME TIMEZONE"
+	case fieldTypeBinary:
+		return "BINARY"
+	case fieldTypeInterval:
+		return "INTERVAL"
+	case fieldTypeIntervalY2M:
+		return "INTERVAL YEAR TO MONTH"
+	case fieldTypeIntervalD2S:
+		return "INTERVAL DAY TO SECOND"
+	case fieldTypeClob:
+		return "CLOB"
+	case fieldTypeBlob:
+		return "BLOB"
+	default:
+		return ""
+	}
+}
+
+/* {{ */
+func (self *FieldDescri) scanType() reflect.Type {
+	switch self.FieldType {
+
+	case fieldTypeBool:
+		return scanTypeBool
+	case fieldTypeTinyint:
+		return scanTypeInt8
+	case fieldTypeShort:
+		return scanTypeInt16
+	case fieldTypeInteger:
+		return scanTypeInt32
+	case fieldTypeBigint:
+		return scanTypeInt64
+	case fieldTypeFloat:
+		return scanTypeFloat32
+	case fieldTypeDouble:
+		return scanTypeFloat64
+	case fieldTypeDate,
+		fieldTypeTime,
+		fieldTypeDatetime:
+		return scanTypeNullTime
+	case fieldTypeTimeTZ,
+		fieldTypeDatetimeTZ,
+		fieldTypeChar,
+		fieldTypeVarChar,
+		fieldTypeBinary,
+		fieldTypeInterval,
+		fieldTypeNumeric,
+		fieldTypeIntervalY2M,
+		fieldTypeIntervalD2S,
+		fieldTypeLob,
+		fieldTypeClob,
+		fieldTypeBlob:
+		return scanTypeRawBytes
+	default:
+		return scanTypeUnknown
+
+	}
+}
+
+var (
+	scanTypeFloat32   = reflect.TypeOf(float32(0))
+	scanTypeFloat64   = reflect.TypeOf(float64(0))
+	scanTypeNullFloat = reflect.TypeOf(sql.NullFloat64{})
+	scanTypeNullInt   = reflect.TypeOf(sql.NullInt64{})
+	scanTypeNullTime  = reflect.TypeOf(time.Time{})
+	scanTypeInt8      = reflect.TypeOf(int8(0))
+	scanTypeInt16     = reflect.TypeOf(int16(0))
+	scanTypeInt32     = reflect.TypeOf(int32(0))
+	scanTypeInt64     = reflect.TypeOf(int64(0))
+	scanTypeUnknown   = reflect.TypeOf(new(interface{}))
+	scanTypeRawBytes  = reflect.TypeOf(sql.RawBytes{})
+	scanTypeUint8     = reflect.TypeOf(uint8(0))
+	scanTypeUint16    = reflect.TypeOf(uint16(0))
+	scanTypeUint32    = reflect.TypeOf(uint32(0))
+	scanTypeUint64    = reflect.TypeOf(uint64(0))
+	scanTypeBool      = reflect.TypeOf(bool(false))
+)

+ 346 - 0
xugu/xugu_param.go

@@ -0,0 +1,346 @@
+package xugu
+
+import "fmt"
+
+//定义 XGCSParam 结构体
+type XGCSParam struct {
+	Type           HANDLE_TYPE // 句柄类型
+	ParamNum       uint32      // 单行参数数量
+	PResourceNum   int         // 参数名称资源数量,步长128
+	ParamArraySize int         // 参数数组长度(数组大小)
+	ValueP         []byte      // 实际参数指针数组,指向参数地址
+	VType          []uint32    // 参数类型:数组,参数数量
+	VParamSize     []uint32    // 参数缓冲区长度,参数数量
+	VDbType        *int        // 参考数据库数据类型,参数数量
+	VActuallenp    [][]int     // 参数数据实际大小数组,[参数数组大小][参数数量]
+	VParamName     [][]byte    // 参数名称
+	VParamNo       []uint32    // 参数序列数组
+	VInOut         []int       // 输入输出类型
+	ErrStr         []byte      // 错误字符串
+	MemType        int         // 内存使用模式(0:引用,1:驱动程序分配)
+	ImpExpType     int         // 0 隐式创建,1 显式创建,2 由服务器提供的准备语句获取
+}
+
+//按序号绑定
+/*=====================================
+p_conn     连接句柄
+param_no   参数号: 从1开始
+param_type 参数输入输出型
+datatype   参数数据类型
+value      参数值
+param_size 单个参数的空间大小 buffer
+rlen_val   具体的每个参数 的对应实际大小
+======================================*/
+func xgSockBindparamByPos(pconn *xuguConn, param_no int, param_type int, datatype int, value []byte, param_size int, rlen_val []int) int {
+	if pconn.Type == HT_CONN {
+		if pconn.params == nil {
+			pconn.params = &XGCSParam{Type: HT_PARAMS}
+		}
+		if param_no > 4000 {
+			pconn.errStr = []byte(fmt.Sprintf("[EC051]Error invalid param NO %d out of range paramnum", param_no))
+			return -51
+		}
+		if !(param_type == 1 || param_type == 2 || param_type == 3 || param_type == 6) {
+			pconn.errStr = []byte(fmt.Sprintf("[EC052]Error invalid param type %d", param_type))
+			return -52
+		}
+		if param_no < 1 {
+			pconn.errStr = []byte(fmt.Sprintf("[EC054]Error param_no  param seq %d invalid", param_no))
+			return -54
+		}
+	} else if pconn.params.Type == HT_PARAMS {
+		if param_no > 4000 {
+			pconn.params.ErrStr = []byte(fmt.Sprintf("[EC051]Error invalid param NO %d out of range paramnum", param_no))
+			//set error 参数绑定序列值 超过合理范围
+			return -51
+		}
+		if !(param_type == 1 || param_type == 2 || param_type == 3 || param_type == 6) {
+			pconn.params.ErrStr = []byte(fmt.Sprintf("[EC052]Error invalid param type %d", param_type))
+			return -52 //参数绑定类型非法
+		}
+		if param_no < 1 {
+			pconn.errStr = []byte(fmt.Sprintf("[EC054]Error param_no  param seq %d invalid", param_no))
+			return -54
+		}
+	} else {
+		//invalid ptr set
+		return XG_INVALID_ARG
+	}
+	ret := xgSockBindParamsByPos(*pconn.params, param_no, param_type, datatype, value, param_size, rlen_val)
+
+	if ret < 0 {
+		if ret == -55 {
+			pconn.errStr = []byte(fmt.Sprintf("[EC055]Error bindparam jump long %d invalid", ret))
+			return -55
+		} else if ret == -8 {
+			pconn.errStr = []byte(fmt.Sprintf("[EC056]Error bindparam unsurpported ret %d invalid", ret))
+			return -8
+		}
+	}
+	return ret
+}
+
+func xgSockBindParamsByPos(pParams XGCSParam, param_no int, param_type int, datatype int, value []byte, param_size int, rlen_val []int) int {
+	ipar := param_no
+	ipar--
+	if pParams.ParamNum == 0 { // 初始化空间
+		pParams.PResourceNum = 128
+
+		pParams.VType = make([]uint32, 128)
+		pParams.VParamSize = make([]uint32, 128)
+		//pParams.VDbType = make([][]int, 128)
+		pParams.VActuallenp = make([][]int, 128)
+		pParams.VParamNo = make([]uint32, 128)
+		pParams.VInOut = make([]int, 128)
+		pParams.VParamName = nil
+	}
+
+	if int(pParams.ParamNum) < param_no { //分配空间(加增)
+		if pParams.PResourceNum < param_no { //资源分配
+			if pParams.PResourceNum+128 < param_no {
+				return -55 // set error  参数绑定跳跃度过大
+			}
+		}
+		//go 切片会自动分配内存 切片扩容机制
+
+	}
+
+	if param_type == 6 {
+		//函数返回值绑定
+		pParams.VParamNo[uint32(ipar)] = uint32(ipar) //意义不大 或者可以改成valid
+		pParams.VType[uint32(ipar)] = uint32(datatype)
+		pParams.ValueP = value
+		pParams.VParamSize[uint32(ipar)] = uint32(param_size)
+		pParams.VActuallenp[uint32(ipar)] = rlen_val
+		pParams.VInOut[uint32(ipar)] = param_type
+
+	} else if param_type == 1 { //input
+
+		pParams.VParamNo[uint32(ipar)] = uint32(ipar) //意义不大 或者可以改成valid
+		pParams.VType[uint32(ipar)] = uint32(datatype)
+		//tmp ===================== value cpoy ================================
+		pParams.ValueP = value
+		pParams.VActuallenp[uint32(ipar)] = rlen_val //tmp end
+		pParams.MemType = 1
+		pParams.VInOut[uint32(ipar)] = 1
+
+	} else if param_type == 2 || param_type == 3 { //output ||inoutput
+		pParams.VParamNo[uint32(ipar)] = uint32(ipar)
+		pParams.VType[uint32(ipar)] = uint32(datatype)
+
+		pParams.ValueP = value
+		pParams.VParamSize[uint32(ipar)] = uint32(param_size) //buff长度
+		pParams.VActuallenp[uint32(ipar)] = rlen_val
+		pParams.VInOut[uint32(ipar)] = param_type
+	} else { //3
+		return -8 //尚未实现
+		//参数绑定类型不合格
+	}
+	return 0
+}
+
+//批量
+/*=============================================
+p_conn  连接句柄
+param_no  参数号: 从1开始
+param_type 参数输入输出型
+datatype 参数数据类型
+array_size 数组的大小长度
+array_value 参数值数组
+param_size 单个参数的空间大小 buffer
+rlen_val 具体的每个参数 的对应实际大小[与参数值大小一一对应]
+==============================================*/
+func xgSockBindParamArrayByPos(pConn *xuguConn, param_no int, param_num int, param_type int, datatype int, array_size int, array_value []byte, param_size int, rlen_val []int) int {
+	if pConn.Type == HT_CONN {
+		if pConn.params == nil {
+			pConn.params = new(XGCSParam)
+			pConn.params.Type = HT_PARAMS //imp_exp_type= 0;
+		}
+		if param_no > 4000 || param_num > 4000 || param_num < -1 {
+			//set error 参数绑定序列值 超过合理范围
+			pConn.errStr = []byte(fmt.Sprintf("[EC051]Error invalid param NO %d out of range paramnum", param_num))
+			return -51
+		}
+		if !(param_type == 1 || param_type == 2 || param_type == 3 || param_type == 6) {
+			pConn.errStr = []byte(fmt.Sprintf("[EC052]Error invalid param type %d", param_type))
+			return -52 //参数绑定类型非法
+		}
+		if param_no < 1 {
+			pConn.errStr = []byte(fmt.Sprintf("[EC054]Error param_no  param seq %d invalid", param_no))
+			return -54
+		}
+	} else if pConn.Type == HT_PARAMS {
+		//pParams := pConn.params
+		if param_no > 4000 {
+			pConn.params.ErrStr = []byte(fmt.Sprintf("[EC051]Error invalid param NO %d out of range paramnum", param_no))
+			return -51
+		}
+		if !(param_type == 1 || param_type == 2 || param_type == 3 || param_type == 6) {
+			pConn.params.ErrStr = []byte(fmt.Sprintf("[EC052]Error invalid param type %d", param_type))
+			return -52 //参数绑定类型非法
+		}
+		if param_no < 1 {
+			pConn.params.ErrStr = []byte(fmt.Sprintf("[EC054]Error param_no  param seq %d invalid", param_no))
+			return -54
+		}
+	} else {
+		return XG_INVALID_ARG
+	}
+	ret := xgSockBindParamsArrayByPos(pConn.params, param_no, param_num, param_type, datatype, array_size, array_value, param_size, rlen_val)
+	return ret
+}
+
+func xgSockBindParamsArrayByPos(pParams *XGCSParam, param_no int, param_num int, param_type int, datatype int, array_size int,
+	array_value []byte, param_size int, rlen_val []int) int {
+	ipar := param_no - 1
+	if pParams.ParamNum == 0 {
+		//以下部分需要重新封装 使得看起来比较代码简练
+
+		if param_num == 0 { //顶层资源 一次分配128
+			pParams.PResourceNum = 128
+
+			pParams.VType = make([]uint32, 128)
+			pParams.VParamSize = make([]uint32, 128)
+			//pParams.VDbType = make([][]int, 128)
+			pParams.VActuallenp = make([][]int, 128)
+			pParams.VParamNo = make([]uint32, 128)
+			pParams.VInOut = make([]int, 128)
+			pParams.VParamName = nil
+		} else { //已知参数列数
+			pParams.ParamArraySize = array_size //数组容量确定
+
+			pParams.VType = make([]uint32, param_num)
+			pParams.VParamSize = make([]uint32, param_num)
+			//pParams.VDbType = make([][]int, 128)
+			pParams.VActuallenp = make([][]int, param_num) //这个p_params->v_actuallenp[ipar][0]指向传入数组就行了
+			pParams.VParamNo = make([]uint32, param_num)
+			pParams.VInOut = make([]int, param_num)
+			//pParams.VParamName = nil
+		}
+	}
+
+	if pParams.ParamNum < uint32(param_no) {
+		if pParams.PResourceNum < param_no { //资源分配
+
+		}
+		pParams.ParamNum = uint32(param_no) //允许了中间的空洞
+
+	}
+
+	if param_type == 6 { //1236   in out inout return value
+		//函数返回值绑定
+
+	} else if param_type == 1 { //input
+		pParams.VParamNo[ipar] = uint32(param_no) ////意义不大 或者可以改成valid
+		pParams.VType[ipar] = uint32(datatype)
+
+		//off := 0
+		//ptrLen := 0 //varchar长度指示器
+		var ptr []byte
+		copy(pParams.VActuallenp[ipar], rlen_val)
+		if needCopyV(datatype) {
+			//m_ele_siz := getDbTypeLen(datatype)
+			copy(ptr, array_value)
+		} else if datatype == XG_C_CHAR || datatype == XG_C_NCHAR {
+			copy(ptr, array_value)
+		} else {
+			// ptr=(char*)malloc(param_size);//no
+			//memcpy(ptr,array_value,param_size);
+		}
+
+		for irow := 0; irow < array_size; irow++ {
+			switch datatype {
+			// case XG_C_INTEGER, XG_C_FLOAT:
+			// 	pParams.ValueP[irow][ipar] = ptr[off]
+			// 	off++
+
+			// case XG_C_NCHAR, XG_C_CHAR:
+			// 	pParams.ValueP[irow][ipar] = ptr[off]
+			// 	off++
+
+			// case XG_C_BIGINT, DATETIME_ASLONG:
+			// 	pParams.ValueP[irow][ipar] = ptr[off]
+			// 	off++
+			// case XG_C_DOUBLE:
+			// 	pParams.ValueP[irow][ipar] = ptr[off]
+			// 	off++
+			// case XG_C_NUMERIC:
+			// 	pParams.ValueP[irow][ipar] = ptr[off]
+			// 	off++
+			// case XG_C_DATE, XG_C_TIME, XG_C_DATETIME, XG_C_TIME_TZ, XG_C_DATETIME_TZ, XG_C_BINARY, XG_C_INTERVAL_YEAR_TO_MONTH, XG_C_INTERVAL_DAY_TO_SECOND:
+			// 	pParams.ValueP[irow][ipar] = ptr[off]
+			// 	off++
+			// case XG_C_SHORT:
+			// 	pParams.ValueP[irow][ipar] = ptr[off]
+			// 	off++
+			// case XG_C_TINYINT, XG_C_BOOL, XG_C_CHARN1:
+			// 	pParams.ValueP[irow][ipar] = ptr[off]
+			// 	off++
+			default:
+				fmt.Println("bindparam unsurpported datatype")
+				pParams.ErrStr = []byte(fmt.Sprintf("[EC058]Error bindparam unsurpported datatype %d invalid", datatype))
+				return -58
+			}
+
+		}
+		pParams.VParamSize[ipar] = uint32(param_size) //buff长度
+		pParams.VInOut[ipar] = 1
+	} else if param_type == 2 { //output
+		//	p_params->v_actuallenp[ipar]    = rlen_val;//这里之所以是引用传递 那是因为 out or inout 型时 需要反馈实际的参数的长度
+		return -8 //尚未实现
+	} else { //3
+		return -8 //尚未实现
+		//参数绑定类型不合格
+	}
+	return XG_OK
+}
+
+//按名绑定
+/*=====================================
+p_conn     连接句柄  2 p_conn  参数句柄(显式创建参数句柄)
+param_name   参数名: 当出现一样的参数名时,会进行覆盖但是不会报错
+param_type 参数输入输出型
+datatype   参数数据类型
+value      参数值
+param_size 单个参数的空间大小 buffer
+rlen_val   具体的每个参数 的对应实际大小
+======================================*/
+func xgSockBindParamByName(pConn *xuguConn, param_name []byte, param_type int, datatype int, void []byte, param_sizeint, rt_code *int, rlen_val []int) int {
+	pParams := pConn.params
+	if pConn.Type == HT_CONN {
+		if nil == pConn.params {
+
+			pConn.params = &XGCSParam{}
+			pConn.params.Type = HT_PARAMS //imp_exp_type= 0;
+		}
+		pParams = pConn.params
+	} else if pConn.Type == HT_PARAMS {
+		pParams = pConn.params
+	} else {
+		//invalid ptr set
+		return XG_INVALID_ARG
+	}
+	if !(param_type == 1 || param_type == 2 || param_type == 3 || param_type == 6) {
+		if pConn.Type == HT_CONN {
+			pConn.params.ErrStr = []byte(fmt.Sprintf("[EC052]Error invalis param type %d", param_type))
+		} else {
+			pParams.ErrStr = []byte(fmt.Sprintf("[EC052]Error invalis param type %d", param_type))
+		}
+		return -52
+	}
+	return 0
+}
+
+func xgSockBindParamByNameSub(pParams *XGCSParam) {
+	if pParams.ParamNum == 0 { //优先考虑 参数不足128个时的情况
+		pParams.PResourceNum = 128
+		pParams.ValueP = make([]byte, 128)
+		pParams.VType = make([]uint32, 128)
+		pParams.VParamSize = make([]uint32, 128)
+		//pParams.VDbType = make([][]int, 128)
+		pParams.VActuallenp = make([][]int, 128)
+		pParams.VParamNo = make([]uint32, 128)
+		pParams.VInOut = make([]int, 128)
+		pParams.VParamName = nil
+	}
+}

+ 410 - 0
xugu/xugu_parse.go

@@ -0,0 +1,410 @@
+package xugu
+
+import (
+	"database/sql/driver"
+	"encoding/binary"
+	"fmt"
+	"strings"
+)
+
+type ParseParam interface {
+	// 转换参数数据类型
+	assertParamType(driver.Value, int) error
+
+	// 解析参数数量
+	assertParamCount(string) int
+
+	// 解析参数绑定类型(按参数名称绑定类型和按参数位置绑定类型)
+	assertBindType(string) int
+
+	// 解析参数名称
+	assertParamName(string) error
+}
+
+// 辅助结构体
+// type xuguValue struct {
+// 	// 布尔值,如果值为 true,表示当前字段的数据类型是大对象数据类型
+// 	islob bool
+
+// 	// 一个 *C.char 类型的指针,通常指向要绑定的参数值的地址
+// 	value []byte
+// 	plob  []byte
+
+// 	// length 通常指定要绑定的参数数据的实际长度
+// 	length []int
+
+// 	// buff 通常指定要绑定的参数数据的内存缓冲区大小
+// 	buffSize int
+
+// 	// 在参数绑定时,指定表中字段的数据类型
+// 	types int
+
+// 	// 返回代码
+// 	rcode int
+// }
+
+type xuguParse struct {
+	// bind_type 用于标识参数绑定的类型。
+	// 参数绑定类型包括按参数名称绑定和按参数占位符绑定
+	bind_type int
+
+	// param_count 用于指定在 SQL 语句中需要绑定的参数数量
+	param_count int
+
+	// 当参数绑定类型为按参数名称绑定时,param_names 是参数名称的集合
+	param_names []byte
+
+	//Val []xuguValue
+	// 当参数绑定类型为按参数占位符绑定时,position 标识参数的位置
+	position int
+}
+
+func (Parse *xuguParse) assertParamCount(query string) int {
+	fmt.Println("----assertParamCount func 判断参数个数")
+	if Parse.bind_type == 0 {
+		Parse.bind_type = Parse.assertBindType(query)
+	}
+	fmt.Println("**Parse.bind_type is", Parse.bind_type)
+	switch Parse.bind_type {
+	case BIND_PARAM_BY_POS:
+		Parse.param_count = strings.Count(query, "?")
+	case BIND_PARAM_BY_NAME:
+		fmt.Println(">case BIND_PARAM_BY_NAME")
+		Parse.param_count = 0
+		pos := 0
+		phead := -1
+
+		for {
+			pos = strings.IndexByte(query[phead+1:], ':')
+			if pos == -1 {
+				break
+			}
+
+			pos += phead + 1
+			tmp := pos
+			for tmp > phead {
+				tmp--
+				if query[tmp] == ' ' {
+					continue
+				}
+
+				if query[tmp] == ',' || query[tmp] == '(' {
+					Parse.param_count++
+				}
+				break
+			}
+			phead = pos
+		}
+	}
+	fmt.Println("**Parse.param_count", Parse.param_count)
+	return Parse.param_count
+}
+
+func (Parse *xuguParse) assertBindType(query string) int {
+	fmt.Println("-----(param *xuguParse) assertBindType 判断绑定类型")
+	Parse.bind_type = strings.IndexByte(query, '?')
+	if Parse.bind_type != -1 {
+		fmt.Println("** 是大对象语句")
+		return BIND_PARAM_BY_POS
+	}
+	fmt.Println("** 不是大对象语句")
+	return BIND_PARAM_BY_NAME
+}
+
+func (param *xuguParse) assertParamType(dV driver.Value, pos int) error {
+	fmt.Println("-----(param *xuguParse) assertParamType 判断参数类型")
+	// var dest xuguValue
+	// switch dV.(type) {
+
+	// case int64:
+	// 	srcv, ok := dV.(int64)
+	// 	if !ok {
+	// 		news := errorNews("int64")
+	// 		return errors.New(news)
+	// 	}
+
+	// 	S := strconv.FormatInt(srcv, 10)
+	// 	dest.value = C.CString(S)
+	// 	dest.length = C.int(strings.Count(S, "") - 1)
+	// 	dest.buff = dest.length + 1
+	// 	dest.islob = false
+	// 	dest.types = SQL_XG_C_CHAR
+
+	// case float32:
+	// 	srcv, ok := dV.(float64)
+	// 	if !ok {
+	// 		news := errorNews("float32")
+	// 		return errors.New(news)
+	// 	}
+
+	// 	S := strconv.FormatFloat(srcv, 'f', 6, 64)
+	// 	dest.value = C.CString(S)
+	// 	dest.length = C.int(strings.Count(S, "") - 1)
+	// 	dest.buff = dest.length + 1
+	// 	dest.islob = false
+	// 	dest.types = SQL_XG_C_CHAR
+
+	// case float64:
+	// 	srcv, ok := dV.(float64)
+	// 	if !ok {
+	// 		news := errorNews("float64")
+	// 		return errors.New(news)
+	// 	}
+
+	// 	S := strconv.FormatFloat(srcv, 'f', 15, 64)
+	// 	dest.value = C.CString(S)
+	// 	dest.length = C.int(strings.Count(S, "") - 1)
+	// 	dest.buff = dest.length + 1
+	// 	dest.islob = false
+	// 	dest.types = SQL_XG_C_CHAR
+
+	// case bool:
+	// 	srcv, ok := dV.(bool)
+	// 	if !ok {
+	// 		news := errorNews("bool")
+	// 		return errors.New(news)
+	// 	}
+
+	// 	S := strconv.FormatBool(srcv)
+	// 	dest.value = C.CString(S)
+	// 	dest.length = C.int(strings.Count(S, "") - 1)
+	// 	dest.buff = dest.length + 1
+	// 	dest.islob = false
+	// 	dest.types = SQL_XG_C_CHAR
+
+	// case string:
+	// 	srcv, ok := dV.(string)
+	// 	if !ok {
+	// 		news := errorNews("string")
+	// 		return errors.New(news)
+	// 	}
+
+	// 	dest.value = C.CString(srcv)
+	// 	dest.length = C.int(strings.Count(srcv, "") - 1)
+	// 	dest.buff = dest.length + 1
+	// 	dest.islob = false
+	// 	dest.types = SQL_XG_C_CHAR
+
+	// 	if dest.length == 0 {
+	// 		dest.length = 1
+	// 	}
+
+	// case time.Time:
+	// 	srcv, ok := dV.(time.Time)
+	// 	if !ok {
+	// 		news := errorNews("time.Time")
+	// 		return errors.New(news)
+	// 	}
+
+	// 	tm := fmt.Sprintf("%04d-%02d-%02d %02d:%02d:%02d",
+	// 		srcv.Year(), int(srcv.Month()), srcv.Day(),
+	// 		srcv.Hour(), srcv.Minute(), srcv.Second())
+
+	// 	dest.value = C.CString(tm)
+	// 	dest.length = C.int(strings.Count(tm, "") - 1)
+	// 	dest.buff = dest.length + 1
+	// 	dest.islob = false
+	// 	dest.types = SQL_XG_C_CHAR
+
+	// case []byte:
+
+	// 	re := cgo_xgc_new_lob(&dest.plob)
+	// 	if re < 0 {
+	// 		return errors.New("Cannot create new large object")
+	// 	}
+
+	// 	srcv, ok := dV.([]byte)
+	// 	if !ok {
+
+	// 		news := errorNews("[]byte")
+	// 		return errors.New(news)
+	// 	}
+
+	// 	cgo_xgc_put_lob_data(
+	// 		&dest.plob,
+	// 		unsafe.Pointer((*C.char)(unsafe.Pointer(&srcv[0]))),
+	// 		len(srcv))
+	// 	cgo_xgc_put_lob_data(&dest.plob, nil, -1)
+
+	// 	dest.value = nil
+	// 	dest.length = C.int(8)
+	// 	dest.buff = C.int(8)
+	// 	dest.islob = true
+	// 	dest.types = SQL_XG_C_BLOB
+
+	// case nil:
+	// 	dest.value = C.CString("xugusql")
+	// 	dest.length = 0
+	// 	dest.buff = C.int(strings.Count("xugusql", ""))
+	// 	dest.islob = false
+	// 	dest.types = SQL_XG_C_CHAR
+
+	// default:
+	// 	/* OTHER DATA TYPE */
+	// 	return errors.New("Unknown data type")
+	// }
+
+	// param.position = pos
+	// param.Val = append(param.Val, dest)
+
+	return nil
+}
+
+func (param *xuguParse) assertParamName(query string) error {
+	fmt.Println("----(param *xuguParse) assertParamName 判断参数名")
+	if param.param_count <= 0 {
+		param.assertParamCount(query)
+	}
+
+	pos := 0
+	phead := -1
+
+	for true {
+		pos = strings.IndexByte(query[phead+1:], ':')
+		if pos == -1 {
+			break
+		}
+
+		pos += phead + 1
+		tmp := pos
+		for tmp > phead {
+			tmp--
+			if query[tmp] == ' ' {
+				continue
+			}
+
+			// Parse parameter positions bound by parameter name
+			if query[tmp] == ',' || query[tmp] == '(' {
+				parg := pos
+				for true {
+					parg++
+					if query[parg] == ',' || query[parg] == ')' || query[parg] == ' ' {
+						param.param_names = append(param.param_names, query[pos+1:parg]...)
+						break
+					}
+				}
+			}
+			break
+		}
+
+		phead = pos
+	}
+
+	return nil
+}
+
+type SelectResult struct {
+	Field_Num uint32
+	Fields    []FieldDescri
+	Values    [][]ValueData //[字段][字段所有值]
+}
+
+type FieldDescri struct {
+	FieldNameLen    int
+	FieldName       string
+	FieldType       fieldType
+	FieldPreciScale int
+	FieldFlag       int
+}
+
+type ValueData struct {
+	Col_len  uint32
+	Col_Data []byte
+}
+
+func parseSelectResult(readBuf buffer) (*SelectResult, error) {
+	data := &SelectResult{}
+
+	char := readBuf.peekChar()
+	fmt.Println("--=char: ", string(char))
+	switch char {
+	case 'A':
+		readBuf.idx++
+		//Field_Num
+		Field_Num := binary.BigEndian.Uint32(readBuf.readNext(4))
+		data.Field_Num = Field_Num
+		fmt.Println("Field_Num: ", data.Field_Num)
+
+		//获取字段信息
+		for i := 0; i < int(Field_Num); i++ {
+
+			field := FieldDescri{}
+
+			//Field_Name_Len
+			field.FieldNameLen = int(binary.BigEndian.Uint32(readBuf.readNext(4)))
+			fmt.Println("FieldNameLen: ", field.FieldNameLen)
+
+			//Field_Name:
+			field.FieldName = string(readBuf.readNext(field.FieldNameLen))
+			fmt.Println("FieldName: ", field.FieldName)
+
+			//Field_DType:
+			field.FieldType = fieldType(binary.BigEndian.Uint32(readBuf.readNext(4)))
+			fmt.Println("FieldType: ", field.FieldType)
+
+			//Field_Preci_Scale:
+			field.FieldPreciScale = int(binary.BigEndian.Uint32(readBuf.readNext(4)))
+			fmt.Println("FieldPreciScale: ", field.FieldPreciScale)
+			//Field_Flag:
+			field.FieldFlag = int(binary.BigEndian.Uint32(readBuf.readNext(4)))
+			fmt.Println("FieldFlag: ", field.FieldFlag)
+
+			data.Fields = append(data.Fields, field)
+		}
+		fmt.Println("data的字段总数: ", data.Field_Num)
+		for i, v := range data.Fields {
+			fmt.Println("data的内容: ", i, v.FieldName)
+		}
+
+		data.Values = make([][]ValueData, data.Field_Num)
+
+		//获取字段的行值
+		// 使用 Peek 方法检查下一个字节是否为'R'或'K'
+		char := readBuf.peekChar()
+		fmt.Println("是否为R: ", string(char))
+		readBuf.idx++
+		if char == 'K' {
+			break
+		} else if char == 'R' {
+
+			colIdx := 0
+			for {
+				col := ValueData{}
+				col.Col_len = binary.BigEndian.Uint32(readBuf.readNext(4))
+				//	fmt.Println("Col_len: ", col.Col_len)
+
+				col.Col_Data = readBuf.readNext(int(col.Col_len))
+				fmt.Println("Col_Data: ", string(col.Col_Data))
+				data.Values[colIdx] = append(data.Values[colIdx], col)
+				colIdx++
+				char := readBuf.peekChar()
+				if char == 'R' {
+					fmt.Println("--=char3: ", string(readBuf.peekChar()))
+					readBuf.idx++
+					colIdx = 0
+					continue
+				} else if char == 'K' {
+					fmt.Println("--=char4: ", string(readBuf.peekChar()))
+					break
+				}
+
+			}
+
+		} else {
+			break
+		}
+
+	}
+	fmt.Println("data的内容: ", data)
+	for i := 0; i < int(data.Field_Num); i++ {
+		for j := 0; j < len(data.Values[i]); j++ {
+			fmt.Printf("data.Values[%d]: %v\n", i, (data.Values[i][j].Col_Data))
+		}
+
+	}
+	return data, nil
+}
+
+func parseInsertResult(readBuf buffer) (*SelectResult, error) {
+
+}

+ 115 - 0
xugu/xugu_prepare.go

@@ -0,0 +1,115 @@
+package xugu
+
+import (
+	"errors"
+	"fmt"
+)
+
+const RETURN_PREPARE_SELECT = 19665
+
+func xgCmdPrepare(pconn *xuguConn, cmd_sql string, prepareName *string) (int, error) {
+	fmt.Println("\n>>>>>xgCmdPrepare")
+	sqlRet := fmt.Sprintf("PREPARE %s AS %s", *prepareName, cmd_sql)
+	//上锁
+	//lockConnect(pconn)
+	pconn.mu.Lock()
+	SockSendCommand0(pconn, sqlRet)
+	var ch byte
+	var ret int
+	for {
+		recvRet, err := rhRecvChar(pconn, &ch)
+		if err != nil {
+			return 0, err
+		}
+		if !recvRet {
+			pconn.mu.Unlock()
+			return -4, errors.New("XG_NET_ERROR")
+		}
+		//A: SelectResult A在协议里为select返回
+		if ch == 'A' {
+			// pres := Result{}
+			// pres.Type = HT_RS
+			// ret, err = recvFiledsInfo(pconn, &pres)
+			// if err != nil {
+			// 	return ret, err
+			// }
+			// pconn.presPrepareCata = &pres
+			ret = RETURN_PREPARE_SELECT
+			continue
+		} else if ch == '$' {
+			var params XGCSParam
+			params.Type = HT_PARAMS
+			params.ImpExpType = 2
+			recvParamsInfo2(pconn, &params)
+			pconn.params = &params
+			continue
+		} else if ch == 'K' {
+			pconn.havePrepare = 1
+		} else {
+			var errStr []byte
+			ret, err = rhRecvStr(pconn, &errStr)
+			if ret < 0 || err != nil {
+				pconn.mu.Unlock()
+				return ret, err
+			}
+			pconn.errStr = errStr
+			ret = XG_ERROR
+			rhRecvChar(pconn, &ch)
+			if ch != 'K' {
+				pconn.bkChar = ch
+				continue
+			}
+		}
+		break
+	}
+	pconn.mu.Unlock()
+	return ret, nil
+}
+func xgCmdUnprepare(pConn *xuguConn, prepareName string) int {
+	fmt.Println("\n>>>>>xgCmdUnprepare")
+	var ch byte
+	var err error
+	ret := XG_OK
+
+	sql := fmt.Sprintf("DEALLOCATE %s ", prepareName)
+	pConn.mu.Lock()
+	SockSendCommand0(pConn, sql)
+	boolRet, err := rhRecvChar(pConn, &ch)
+	fmt.Println("rhRecvChar => ch ", ch)
+	if boolRet || err != nil {
+		ret = XG_NET_ERROR
+	} else if ch == 'K' {
+		pConn.havePrepare = 0
+	} else {
+		pConn.havePrepare = 0
+		var errStre []byte
+		ret, err = rhRecvStr(pConn, &errStre)
+		if err != nil || ret < 0 {
+			pConn.mu.Unlock()
+			return ret
+		}
+
+		rhRecvChar(pConn, &ch)
+		ret = XG_ERROR
+	}
+	pConn.mu.Unlock()
+	return 0
+}
+
+// execute for prepare2
+/* XGC_Execute2 综合执行文件 与 prepare2配合使用
+* p_conn 连接句柄
+* prepare_name 准备的prepare, 如果 prepare2(conn,NULL)  那么这里就是执行 XGC_Execute2(p_conn,NULL,NULL,&p_result)
+* servercursor_name  ,生成的服务器端游标, 如果 prepare2(conn,prepare_name) , 那么这里就是执行 XGC_Execute2(p_conn,prepare_name,servercursor_name,NULL)
+* p_res 结果集指针, 其中 servercursor_name 与 pres 不同时并存
+ */
+func XGC_Execute(pConn *xuguConn, sql string) int {
+	fmt.Println("\n>>>>>XGC_Execute")
+	//发送没有参数的sql
+	SockSendCommand0(pConn, sql)
+	return 0
+}
+
+func send_params(p_conn *xuguConn, p_params *XGCSParam) {
+	fmt.Println("--- send_params(p_conn *xuguConn, p_params *XGCSParam)")
+}

+ 84 - 0
xugu/xugu_result.go

@@ -0,0 +1,84 @@
+package xugu
+
+import "fmt"
+
+type xuguResult struct {
+
+	// Returns the number of rows affected
+	// by update, delete and other related operations
+	affectedRows int64
+
+	// Returns the GUID number of
+	// the insert operation (not supported)
+	insertId int64
+}
+
+// LastInsertId returns the integer generated by the database
+// in response to a command. Typically this will be from an
+// "auto increment" column when inserting a new row. Not all
+// databases support this feature, and the syntax of such
+// statements varies.
+func (self *xuguResult) LastInsertId() (int64, error) {
+	fmt.Println(">>>>>(self *xugusqlResult) LastInsertId()")
+	return self.insertId, nil
+}
+
+// RowsAffected returns the number of rows affected by an
+// update, insert, or delete. Not every database or database
+// driver may support this.
+func (self *xuguResult) RowsAffected() (int64, error) {
+	fmt.Println(">>>>>(self *xugusqlResult) RowsAffected()")
+	return self.affectedRows, nil
+}
+
+// type FieldInfo struct {
+// 	TabName string // 基表名
+// 	Name    string // 列名
+// 	Alias   string // 别名
+// 	TypeID  uint32 // 类型 ID
+// 	CTypeID uint32 // ODBC 的 SQL_C_XXX 类型
+// 	Modi    uint32 // 列修改集合
+// 	Flags   uint32 // 列标志,用于是否可为空,是否为主键等
+// 	Offset  uint32 // 列数据偏移量
+// }
+
+// type RhRow struct {
+// 	FieldNum int16    // 字段数量
+// 	State    int16    // 状态
+// 	Mbmk     [20]byte // 标记
+// 	RowData  [4]byte  // 行数据(长度不固定)
+// }
+
+// type Block struct {
+// 	Next     *Block // 下一个块指针
+// 	BuffSize int    // 缓冲区大小
+// 	Off      int    // 偏移量
+// 	Res      int    // 资源
+// 	BlkNo    int    // 块编号(未使用)
+// 	RowNo    int64  // 行号(未使用)
+// 	Data     []byte // 数据
+// }
+// type RSMemStruct struct {
+// 	Head    *Block // 头部块
+// 	CurBlok *Block // 当前块
+// }
+// type Result struct {
+// 	Type          HANDLE_TYPE       // 句柄类型
+// 	State         int32             // 状态
+// 	FieldNum      uint32            // 字段数量
+// 	ColInfos      []FieldInfo       // SELECT 返回的结果集列属性信息
+// 	RowSize       uint32            // 行大小
+// 	ICurrPos      int32             // 当前行位置
+// 	SlotNum       int64             // 槽数量
+// 	Rows          [][][]interface{} // 行数据
+// 	RowNum        int64             // 记录计数器
+// 	CurRec        *RhRow            // 当前记录
+// 	ErrStr        string            // 错误信息字符串
+// 	DbcFlob       *xuguConn         // 数据库连接属性
+// 	IsMultiResult bool              // 是否多结果集
+// 	NextResult    *Result           // 下一个结果集
+// 	PBlokmemls    *RSMemStruct      // 内存块链表,添加于 2019-05-06
+// 	SQLType       int               // SQL 类型:0 未知, 1 插入, 2 更新, 3 删除, 4 查询, 5 其他, 6 创建, 7 修改, 8 开始, 9 声明, 10 设置
+// 	EffectNum     int               // 更新、删除类型的影响行数
+// 	InsertBmk     [32]byte          // 插入类型的标记
+// }

+ 117 - 0
xugu/xugu_rows.go

@@ -0,0 +1,117 @@
+package xugu
+
+// /查询结果:driver.Rows
+
+import (
+	"database/sql/driver"
+	"fmt"
+	"reflect"
+)
+
+// type Row struct {
+// 	// Data information storage carrier of each column in the result set
+// 	columns []FieldDescri
+// 	// The name of each column in the result set of the current query
+// 	names []string
+// 	done  bool
+// }
+
+type xuguRows struct {
+	// A context handle pointer, which can be used to obtain
+	// information about the result set
+	//result  unsafe.Pointer
+	results *SelectResult
+	// The return value of the function cgo_xgc_read_next()
+	lastRowRelt int
+
+	// The return value of the function cgo_xgc_next_result()
+	lastRelt int
+	// Boolean value, used to identify whether the executed
+	// SQL statement has been prepared
+	prepared bool
+
+	// Context connection handle pointer
+	rows_conn *xuguConn
+	//rowset    Row
+}
+
+// Next用于把数据集中的下一行填入提供的slice中。该slice的
+// 长度应该跟Columns()的长度一致
+//
+// 当没有数据行时,Next应该返回io.EOF
+//
+// dest不应被声明在Next之外(应该作为Next的一个成员变量)
+// 关闭Rows时应特别注意,不要修改dest中缓冲区的值
+func (row *xuguRows) Next(dest []driver.Value) error {
+	fmt.Println(">>>>>(self *xuguRows) Next() ")
+	fmt.Println(" -- int(row.results.Field_Num)", int(row.results.Field_Num))
+	for j := 0; j < int(row.results.Field_Num); j++ {
+
+		coluType := row.results.Fields[j].FieldType
+		fmt.Println("  --coluType: ", coluType, row.results.Fields[j].typeDatabaseName())
+
+		switch coluType {
+
+		case fieldTypeDate:
+
+		case fieldTypeBinary, fieldTypeLob,
+			fieldTypeClob, fieldTypeBlob:
+
+		case fieldTypeTime,
+			fieldTypeTimeTZ:
+
+		case fieldTypeDatetime,
+			fieldTypeDatetimeTZ:
+
+		default:
+			fmt.Println("   ----(self *xuguRows) Next() default ")
+			dest[j] = make([]byte, len(row.results.Values))
+			dest[j] = row.results.Values[j][0].Col_Data
+			fmt.Println(" dest[j]:", dest[j])
+			fmt.Println(" row.results.Values[j][0].Col_Data:", string(row.results.Values[j][0].Col_Data))
+		}
+	}
+	fmt.Println(" -dest: ", dest)
+	return nil
+}
+
+// Columns返回列的名字集,它的个数是从slice的长度中推断出来的。
+// 如果不知道特定的列名,应该为该条目返回一个空的字符串
+func (row *xuguRows) Columns() []string {
+
+	fmt.Println(">>>>>(self *xuguRows) Columns() ")
+	// if row.results == nil {
+	// 	return nil
+	// }
+	// if row.results.Fields != nil {
+	// 	//return self.results.Fields
+	// }
+
+	//columns := make([]string, int(row.results.Field_Num))
+	var columns []string
+	fmt.Println("字段总数row.results.Field_Num", int(row.results.Field_Num))
+	for i, v := range row.results.Fields {
+		columns = append(columns, v.FieldName)
+		fmt.Println("ppend(columns, v.FieldName)", i, columns[i])
+	}
+
+	fmt.Println(" -columns len  ", len(columns))
+
+	for i, v := range columns {
+
+		fmt.Println("  -columns:  ", i, v)
+	}
+	return columns
+}
+
+func (row *xuguRows) ColumnTypeScanType(index int) reflect.Type {
+	fmt.Println(">>>>>ColumnTypeScanType ")
+	fmt.Printf("  -- row.results.Fields %#v \n", row.results.Fields)
+
+	return row.results.Fields[index].scanType()
+}
+
+func (self *xuguRows) Close() error {
+	fmt.Println(">>>>>((self *xuguRows) Close() ")
+	return nil
+}

+ 70 - 0
xugu/xugu_sock.go

@@ -0,0 +1,70 @@
+package xugu
+
+import (
+	"context"
+	"errors"
+	"fmt"
+)
+
+func xgSockOpenConn(ctx context.Context, pConn *xuguConn) (int, error) {
+	fmt.Println(">>>>>XGSOpenConn")
+	//发送
+	//fmt.Printf("login   database = '%s' user = '%s'  password = '%s' version='201' ", pConn.Database, pConn.User, pConn.Password)
+	//	message := "login   database = 'SYSTEM' user = 'SYSDBA'  password = 'SYSDBA' version='201' "
+	dsnMessage := generateLoginString(pConn.dsnConfig)
+	_, err := pConn.conn.Write([]byte(dsnMessage))
+	if err != nil {
+		fmt.Println("conn 发送数据库连接失败:", err)
+	}
+	fmt.Println("数据已发送:", dsnMessage)
+
+	buffer := make([]byte, 1024)
+	n, err := pConn.conn.Read(buffer)
+	if err != nil {
+		fmt.Println("Error reading from xuguserver:", err)
+	}
+	fmt.Println("读取数据库服务返回:", string(buffer[:n]))
+	//SockSendCommand0(pConn, "set schema SYSTEM")
+	return 1, nil
+}
+
+func xgSockPrepare(pConn *xuguConn, sql string, prepareName *string) (int, error) {
+	fmt.Println(">>>>>xgSockPrepare")
+
+	if "" == *prepareName {
+		ret := 0
+		//p_params := &conn.params
+		if pConn.havePrepare != 0 {
+			ret = xgCmdUnprepare(pConn, pConn.prepareName)
+			if ret < 0 {
+				return ret, errors.New("XG_ERROR")
+			}
+			pConn.prepareName = ""
+
+			pConn.prepareName = fmt.Sprintf(pConn.prepareName+"STC%d", pConn.prepareNo)
+			ret, err := xgCmdPrepare(pConn, sql, &pConn.prepareName)
+			if err != nil {
+				return ret, err
+			}
+		}
+		return ret, errors.New("xgSockPrepare_ERROR")
+	}
+
+	ret := 0
+
+	//	XGCSParam* p_params= pconn->params;
+	old_p := pConn.havePrepare
+	//	char* sql=strdup(cmd_sql);
+
+	*prepareName = fmt.Sprintf("STC%d", pConn.prepareNo)
+	pConn.prepareNo++ // 递增 prepare_no 的值
+
+	ret, err := xgCmdPrepare(pConn, sql, &pConn.prepareName)
+	if err != nil {
+		return 0, err
+	}
+	pConn.havePrepare = old_p // this keeped by prepare_name
+
+	return ret, nil
+
+}

+ 305 - 0
xugu/xugu_sock_recv.go

@@ -0,0 +1,305 @@
+package xugu
+
+import (
+	"bufio"
+	"encoding/binary"
+	"errors"
+	"fmt"
+	"net"
+)
+
+func xgSockRecvCommand(pConn *xuguConn) {
+
+}
+
+func xgSockRecvData(conn net.Conn, buffer *[]byte) (int, error) {
+	// 接收服务器的返回消息
+	n, err := conn.Read(*buffer)
+	if err != nil {
+		fmt.Println("接收服务器消息错误Error reading from server:", err)
+		return 0, err
+	}
+	return n, nil
+}
+
+// TODO : pConn.bkChar != 0x0  这一部分代码需要修改
+func rhRecv(pConn *xuguConn, buff []byte, dLen uint32) error {
+	//old_len := dLen
+	if pConn.bkChar != 0x0 {
+		buff[0] = pConn.bkChar
+		pConn.bkChar = 0x0
+		//buff = buff[1:]
+		dLen--
+	}
+	//接收加密登录信息
+	if dLen != 0 {
+		for dLen != 0 {
+			if pConn.useSSL {
+				buffer := make([]byte, 1024)
+				n, err := pConn.conn.Read(buffer)
+				if err != nil {
+					return err
+				} else {
+					//xgCacheRecv(pConn, buffer, int32(n))
+				}
+				if n <= 0 {
+					return errors.New("read error")
+				}
+
+			}
+		}
+		//	DECRYPT(p_conn, buff, old_len); //解密缓冲
+	}
+	return nil
+}
+
+// 接收单个字符
+func rhRecvChar(pConn *xuguConn, valp *byte) (bool, error) {
+
+	var ch [1]byte
+	err := rhRecv(pConn, ch[:], 1)
+	if err != nil {
+		return false, err
+	}
+	fmt.Println("ch[] = ", ch[:])
+	*valp = ch[0]
+	return true, nil
+
+}
+
+//读取检查一个字符
+
+func rhRecvStr(pConn *xuguConn, valp *[]byte) (int, error) {
+	var len uint32
+	if err := rhRecvInt32(pConn, &len); err != nil {
+		return -4, err
+	}
+	pBuff := make([]byte, len+1)
+	if err := rhRecv(pConn, pBuff, len); err != nil {
+		return -4, err
+	}
+	pBuff[len] = 0x0
+	*valp = pBuff
+	return 0, nil
+}
+func rhRecvInt32(pconn *xuguConn, i *uint32) error {
+	var buff [4]byte
+	err := rhRecv(pconn, buff[:], 4)
+	if err != nil {
+		return err
+	}
+
+	// 将大端字节序转换为主机字节序
+	*i = binary.BigEndian.Uint32(buff[:])
+
+	return nil
+}
+
+// // 从连接中接收字段信息,并将其存储在 Result 结构体中。
+// func recvFiledsInfo(pConn *xuguConn, pRes *Result) (int, error) {
+// 	var fieldNum uint32
+
+// 	//var pRet *FieldInfo
+// 	if err := rhRecvInt32(pConn, &fieldNum); err != nil {
+// 		return XG_NET_ERROR, err
+// 	}
+// 	if fieldNum > 4000 {
+// 		return XG_NET_ERROR, errors.New("fieldNum >4000")
+// 	}
+
+// 	pRes.FieldNum = fieldNum
+
+// 	pRet := make([]FieldInfo, fieldNum)
+// 	//接收字段详细信息
+// 	for n := uint32(0); n < fieldNum; n++ {
+// 		if ret, err := recvAttrDesItem(pConn, &pRet[n]); err != nil {
+// 			return ret, err
+// 		}
+// 	}
+
+// 	bytesN := (fieldNum*2 + 7) / 8
+// 	for n := uint32(0); n < fieldNum; n++ {
+// 		pRet[n].Offset = bytesN
+// 		bytesN += uint32(getSQLCType(pRet[n].TypeID))
+// 	}
+// 	// TODO : 这里需要重新修改
+// 	// 在c代码中这里还加上了 ROW_HEAD_SIZE
+// 	pRes.RowSize = bytesN + 24
+// 	pRes.ColInfos = pRet
+
+// 	return 0, nil
+// }
+
+// // 接收字段属性描述
+// func recvAttrDesItem(pConn *xuguConn, pItem *FieldInfo) (int, error) {
+// 	var nameLen uint32
+// 	var tabName []byte
+// 	var alias []byte
+
+// 	if err := rhRecvInt32(pConn, &nameLen); err != nil {
+// 		return XG_NET_ERROR, err
+// 	}
+// 	if nameLen > 1024 {
+// 		return XG_NET_ERROR, errors.New("nameLen >1024")
+// 	}
+// 	var name []byte
+// 	if err := rhRecv(pConn, name, nameLen); err != nil {
+// 		return XG_NET_ERROR, err
+// 	}
+
+// 	if ret := bytes.IndexByte(name, '%'); ret != -1 {
+// 		alias = name[ret+1:]
+// 		name = name[:ret-1]
+// 	}
+
+// 	if ret := bytes.IndexByte(name, '.'); ret != -1 {
+// 		//如果 return_schema_on 为真,那么这里将得到 schema_name.tab_name。
+// 		tabName = name[:ret-1]
+// 		name = name[ret+1:]
+// 	}
+
+// 	pItem.TabName = string(tabName)
+// 	pItem.Name = string(name)
+// 	pItem.Alias = string(alias)
+
+// 	if err := rhRecvInt32(pConn, &pItem.TypeID); err != nil {
+// 		return XG_NET_ERROR, err
+// 	}
+// 	if err := rhRecvInt32(pConn, &pItem.Modi); err != nil {
+// 		return XG_NET_ERROR, err
+// 	}
+// 	if err := rhRecvInt32(pConn, &pItem.Flags); err != nil {
+// 		return XG_NET_ERROR, err
+// 	}
+
+// 	pItem.CTypeID = uint32(getSQLCType(pItem.TypeID))
+
+// 	return 0, nil
+// }
+
+func readInt32(reader *bufio.Reader) (int, error) {
+	var value int32
+	err := binary.Read(reader, binary.BigEndian, &value)
+	return int(value), err
+}
+
+func recvParamsInfo2(pConn *xuguConn, params *XGCSParam) (int, error) {
+	var paramNum uint32
+	var i_ord []uint32
+	var i_type []uint32
+	var i_prescale []uint32
+	pNames := make([][]byte, paramNum)
+	rhRecvInt32(pConn, &paramNum)
+	params.ParamNum = paramNum
+
+	for n := uint32(0); n < paramNum; n++ {
+		// 读取名称
+		rhRecvStr(pConn, &pNames[n])
+		// 读取序号
+		rhRecvInt32(pConn, &i_ord[n])
+		// 读取类型
+		rhRecvInt32(pConn, &i_type[n])
+		// 读取预缩放值
+		rhRecvInt32(pConn, &i_prescale[n])
+	}
+
+	params.VParamName = pNames
+	params.VParamNo = i_ord
+	params.VType = i_type
+	params.VParamSize = i_prescale
+
+	return 0, nil
+}
+
+//接收结果集
+//int  recv_Query_Rs(Conn_Attrs* p_conn,Result** pp_res,int*field_num,int64* rowcount,int* effected_num)
+/* recv_Query_Rs :
+* p_conn conn inout
+* pp_res  output
+ */
+// func recvQueryRs(pConn *xuguConn, ppRes **Result) int {
+// 	ret := 0
+// 	pres := &Result{}
+// 	pres_H := &Result{}
+// 	for {
+// 		var ch byte
+// 		rhRecvChar(pConn, &ch)
+// 		switch ch {
+// 		case 'k':
+// 			if pres_H != nil {
+// 				*ppRes = pres_H
+// 			} else {
+// 				*ppRes = pres
+// 			}
+// 			return ret
+
+// 		case 'E', 'F':
+// 			var errStr []byte
+// 			rhRecvStr(pConn, &errStr)
+// 			fmt.Printf("[EC031]Error in recv result :%s \n", errStr)
+// 			pConn.errStr = errStr
+// 			ret = XG_ERROR
+// 			continue
+
+// 		case 'W', 'M':
+// 			var errStr []byte
+// 			rhRecvStr(pConn, &errStr)
+// 			fmt.Printf("[EC032]Server Warning in recv result :%s \n", errStr)
+// 			pConn.errStr = errStr
+// 			ret = XG_ERROR // cyj 是否有点错误
+// 		case 'I':
+// 			//var len uint32
+// 			// if !rhRecvInt32(pConn, &len) {
+
+// 			// }
+
+// 		case 'U', 'D':
+// 		case 'A':
+// 			if pres == nil {
+// 				pres := &Result{}
+// 				pres.Type = HT_RS
+// 			} else {
+// 				var prev = &Result{}
+// 				if pres_H != nil {
+// 					prev = pres_H
+// 					for prev.NextResult != nil {
+// 						prev = prev.NextResult
+// 					}
+// 				} else {
+// 					pres_H = pres
+// 					prev = pres_H
+// 				}
+// 				pres = &Result{}
+// 				pres.Type = HT_RS
+// 				prev.NextResult = pres
+// 			}
+// 			pres.SQLType = SQL_SELECT
+// 			pres.DbcFlob = pConn
+
+// 			// _, err := recvFiledsInfo(pConn, pres)
+// 			// if err != nil {
+// 			// 	return XG_SOCKET_ERROR,
+// 			// }
+
+// 		}
+
+// 	}
+// 	return 0
+// }
+
+// recv_record
+// func recvRecords20000(p_conn *Conn_Attrs, p_res *Result) int {
+// 	pRow := RhRow{}
+
+// 	// if p_res.PBlokmemls == nil{
+// 	// 	p_res.PBlokmemls =
+// 	// }
+
+// 	for i := 0; i < 20000; i++ {
+// 		//recvRecord(p_conn, p_res, &pRow)
+// 	}
+// }
+
+func recvRecord() {
+
+}

+ 306 - 0
xugu/xugu_sock_recv2.go

@@ -0,0 +1,306 @@
+package xugu
+
+import (
+	"bufio"
+	"io"
+)
+
+// ----------------------------------------------------------------------------------------------------
+type SrvResponse struct {
+	FormArgDescri []*FormArgDescri
+	SelectResult  []*SelectResult
+	InsertResult  []*InsertResult
+	UpdateResult  []*UpdateResult
+	DeleteResult  []*DeleteResult
+	ProcRet       []*ProcRet
+	OutParamRet   []*OutParamRet
+	ErrInfo       []*ErrInfo
+	WarnInfo      []*WarnInfo
+	Message       []*Message
+}
+
+type FormArgDescri struct {
+	ArgNum int
+	Args   []ArgDescri
+}
+
+type ArgDescri struct {
+	ArgNameLen    int
+	ArgName       string
+	ArgNo         int
+	ArgDType      int
+	ArgPreciScale int
+}
+
+type InsertResult struct {
+	RowidLen  int
+	RowidData []byte
+}
+
+type UpdateResult struct {
+	UpdateNum int
+}
+
+type DeleteResult struct {
+	DeleteNum int
+}
+
+type ProcRet struct {
+	RetDType   int
+	RetDataLen int
+	RetData    []byte
+}
+
+type OutParamRet struct {
+	OutParamNo    int
+	OutParamDType int
+	OutParamLen   int
+	OutParamData  []byte
+}
+
+type ErrInfo struct {
+	ErrStrLen int
+	ErrStr    string
+}
+
+type WarnInfo struct {
+	WarnStrLen int
+	WarnStr    string
+}
+
+type Message struct {
+	MsgStrLen int
+	MsgStr    string
+}
+
+// func parseResponse(conn net.Conn) (*SrvResponse, error) {
+// 	response := &SrvResponse{}
+// 	reader := bufio.NewReader(conn)
+
+// 	for {
+// 		msgType, err := reader.ReadByte()
+// 		if err != nil {
+// 			return nil, err
+// 		}
+
+// 		switch msgType {
+// 		case '$':
+// 			argDescri, err := parseFormArgDescri(reader)
+// 			if err != nil {
+// 				return nil, err
+// 			}
+// 			response.FormArgDescri = append(response.FormArgDescri, argDescri)
+
+// 		case 'I':
+// 			insertResult, err := parseInsertResult(reader)
+// 			if err != nil {
+// 				return nil, err
+// 			}
+// 			response.InsertResult = append(response.InsertResult, insertResult)
+// 		case 'U':
+// 			updateResult, err := parseUpdateResult(reader)
+// 			if err != nil {
+// 				return nil, err
+// 			}
+// 			response.UpdateResult = append(response.UpdateResult, updateResult)
+// 		case 'D':
+// 			deleteResult, err := parseDeleteResult(reader)
+// 			if err != nil {
+// 				return nil, err
+// 			}
+// 			response.DeleteResult = append(response.DeleteResult, deleteResult)
+// 		case 'O':
+// 			procRet, err := parseProcRet(reader)
+// 			if err != nil {
+// 				return nil, err
+// 			}
+// 			response.ProcRet = append(response.ProcRet, procRet)
+// 		case 'P':
+// 			outParamRet, err := parseOutParamRet(reader)
+// 			if err != nil {
+// 				return nil, err
+// 			}
+// 			response.OutParamRet = append(response.OutParamRet, outParamRet)
+// 		case 'E':
+// 			errInfo, err := parseErrInfo(reader)
+// 			if err != nil {
+// 				return nil, err
+// 			}
+// 			response.ErrInfo = append(response.ErrInfo, errInfo)
+// 		case 'W':
+// 			warnInfo, err := parseWarnInfo(reader)
+// 			if err != nil {
+// 				return nil, err
+// 			}
+// 			response.WarnInfo = append(response.WarnInfo, warnInfo)
+// 		case 'M':
+// 			message, err := parseMessage(reader)
+// 			if err != nil {
+// 				return nil, err
+// 			}
+// 			response.Message = append(response.Message, message)
+// 		case 'K':
+// 			return response, nil
+// 		default:
+// 			return nil, fmt.Errorf("未知的消息类型: %v", msgType)
+// 		}
+// 	}
+// }
+
+// 解析函数的实现,例如 parseFormArgDescri 等
+
+func parseFormArgDescri(reader *bufio.Reader) (*FormArgDescri, error) {
+	argNum, err := readInt32(reader)
+	if err != nil {
+		return nil, err
+	}
+	formArgDescri := &FormArgDescri{ArgNum: argNum}
+	for i := 0; i < argNum; i++ {
+		argDescri := ArgDescri{}
+		argDescri.ArgNameLen, err = readInt32(reader)
+		if err != nil {
+			return nil, err
+		}
+		argDescri.ArgName, err = readString(reader, argDescri.ArgNameLen)
+		if err != nil {
+			return nil, err
+		}
+		argDescri.ArgNo, err = readInt32(reader)
+		if err != nil {
+			return nil, err
+		}
+		argDescri.ArgDType, err = readInt32(reader)
+		if err != nil {
+			return nil, err
+		}
+		argDescri.ArgPreciScale, err = readInt32(reader)
+		if err != nil {
+			return nil, err
+		}
+		formArgDescri.Args = append(formArgDescri.Args, argDescri)
+	}
+	return formArgDescri, nil
+}
+
+func readString(reader *bufio.Reader, length int) (string, error) {
+	bytes := make([]byte, length)
+	_, err := reader.Read(bytes)
+	return string(bytes), err
+}
+
+func parseFieldDescri(reader *bufio.Reader) (FieldDescri, error) {
+	fieldDescri := FieldDescri{}
+	var err error
+
+	fieldDescri.FieldNameLen, err = readInt32(reader)
+	if err != nil {
+		return fieldDescri, err
+	}
+	fieldDescri.FieldName, err = readString(reader, fieldDescri.FieldNameLen)
+	if err != nil {
+		return fieldDescri, err
+	}
+	// fieldDescri.FieldType, err = fieldType(reader)
+	// if err != nil {
+	// 	return fieldDescri, err
+	// }
+	fieldDescri.FieldPreciScale, err = readInt32(reader)
+	if err != nil {
+		return fieldDescri, err
+	}
+	fieldDescri.FieldFlag, err = readInt32(reader)
+	if err != nil {
+		return fieldDescri, err
+	}
+
+	return fieldDescri, nil
+}
+
+func parseUpdateResult(reader *bufio.Reader) (*UpdateResult, error) {
+	updateNum, err := readInt32(reader)
+	if err != nil {
+		return nil, err
+	}
+	return &UpdateResult{UpdateNum: updateNum}, nil
+}
+func parseDeleteResult(reader *bufio.Reader) (*DeleteResult, error) {
+	deleteNum, err := readInt32(reader)
+	if err != nil {
+		return nil, err
+	}
+	return &DeleteResult{DeleteNum: deleteNum}, nil
+}
+func parseProcRet(reader *bufio.Reader) (*ProcRet, error) {
+	retDType, err := readInt32(reader)
+	if err != nil {
+		return nil, err
+	}
+	retDataLen, err := readInt32(reader)
+	if err != nil {
+		return nil, err
+	}
+	retData := make([]byte, retDataLen)
+	_, err = io.ReadFull(reader, retData)
+	if err != nil {
+		return nil, err
+	}
+	return &ProcRet{RetDType: retDType, RetDataLen: retDataLen, RetData: retData}, nil
+}
+func parseOutParamRet(reader *bufio.Reader) (*OutParamRet, error) {
+	outParamNo, err := readInt32(reader)
+	if err != nil {
+		return nil, err
+	}
+	outParamDType, err := readInt32(reader)
+	if err != nil {
+		return nil, err
+	}
+	outParamLen, err := readInt32(reader)
+	if err != nil {
+		return nil, err
+	}
+	outParamData := make([]byte, outParamLen)
+	_, err = io.ReadFull(reader, outParamData)
+	if err != nil {
+		return nil, err
+	}
+	return &OutParamRet{
+		OutParamNo:    outParamNo,
+		OutParamDType: outParamDType,
+		OutParamLen:   outParamLen,
+		OutParamData:  outParamData,
+	}, nil
+}
+func parseErrInfo(reader *bufio.Reader) (*ErrInfo, error) {
+	errStrLen, err := readInt32(reader)
+	if err != nil {
+		return nil, err
+	}
+	errStr, err := readString(reader, errStrLen)
+	if err != nil {
+		return nil, err
+	}
+	return &ErrInfo{ErrStrLen: errStrLen, ErrStr: errStr}, nil
+}
+func parseWarnInfo(reader *bufio.Reader) (*WarnInfo, error) {
+	warnStrLen, err := readInt32(reader)
+	if err != nil {
+		return nil, err
+	}
+	warnStr, err := readString(reader, warnStrLen)
+	if err != nil {
+		return nil, err
+	}
+	return &WarnInfo{WarnStrLen: warnStrLen, WarnStr: warnStr}, nil
+}
+func parseMessage(reader *bufio.Reader) (*Message, error) {
+	msgStrLen, err := readInt32(reader)
+	if err != nil {
+		return nil, err
+	}
+	msgStr, err := readString(reader, msgStrLen)
+	if err != nil {
+		return nil, err
+	}
+	return &Message{MsgStrLen: msgStrLen, MsgStr: msgStr}, nil
+}

+ 169 - 0
xugu/xugu_sock_send.go

@@ -0,0 +1,169 @@
+package xugu
+
+import (
+	"encoding/binary"
+	"fmt"
+)
+
+func SockSendCommandN(pConn *xuguConn, sql string, num int) (int, error) {
+	ret := 0
+	var err error
+	cmd_len := len(sql)
+	ret, err = RhSend(pConn, []byte("?"), 1)
+	if err != nil {
+		return 0, err
+	}
+	ret, err = rhSendInt32(pConn, uint32(cmd_len))
+	if err != nil {
+		return 0, err
+	}
+	ret, err = RhSend(pConn, []byte(sql), cmd_len+1)
+	if err != nil {
+		return 0, err
+	}
+	if num != 0 {
+		ret, err = rhSendInt32(pConn, uint32(num)) //param num
+		if err != nil {
+			return 0, err
+		}
+	} else {
+		ret, err = rhSendInt32(pConn, uint32(0))
+		if err != nil {
+			return 0, err
+		}
+	}
+
+	return ret, nil
+}
+
+func SockSendCommand0(pConn *xuguConn, sql string) (int, error) {
+	cmdLen := len(sql)
+	//发送问号字符 ?
+	ret, err := RhSend(pConn, []byte("?"), cmdLen)
+	if err != nil {
+		return 0, err
+	}
+	// Comand_Len
+	lengthInt32 := uint32(len(sql))
+	ret, err = rhSendInt32(pConn, lengthInt32)
+	if err != nil {
+		return 0, err
+	}
+	//  Comand_str
+	ret, err = RhSend(pConn, []byte(sql), cmdLen+1)
+	if err != nil {
+		return 0, err
+	}
+
+	data := make([]byte, 1)
+	ret, err = RhSend(pConn, data, 1)
+	if err != nil {
+		return 0, err
+	}
+
+	// Param_num
+	ret, err = rhSendInt32(pConn, 0)
+	if err != nil {
+		return 0, err
+	}
+	fmt.Println("SockSendCommand0 msg", pConn.sendBuff.String())
+
+	RhFlush(pConn)
+
+	return ret, nil
+}
+
+// RhSend Go 版本的 rh_send 函数
+// TODO :加密连接还没有实现
+func RhSend(pConn *xuguConn, buff []byte, length int) (int, error) {
+	pConn.mu.Lock()
+	defer pConn.mu.Unlock()
+	//没有内容则不发送
+	if length == 0 {
+		return 0, nil
+	}
+	//连接里是否有加密
+	if pConn.useSSL {
+		fmt.Println("加密未实现")
+		return length, nil
+		// if length > 2*1024 {
+		// 	EncryptBuff(pconn, buff)
+		// 	if _, err := pconn.conn.Write(pconn.sendBuff.Bytes()); err != nil {
+		// 		pconn.sendBuff.Reset()
+		// 		return 0, err
+		// 	}
+		// 	pconn.sendBuff.Reset()
+		// 	_, err := pconn.conn.Write(bufPrintln()
+		// 	return len(buff), err
+		// } else if pconn.sendBuff.Len()+length >= BUFF_SIZE {
+		// 	if _, err := pconn.conn.Write(pconn.sendBuff.Bytes()); err != nil {
+		// 		pconn.sendBuff.Reset()
+		// 		return 0, err
+		// 	}
+		// 	pconn.sendBuff.Reset()
+		// 	_, err := pconn.sendBuff.Write(buff)
+		// 	if err != nil {
+		// 		return 0, err
+		// 	}
+		// 	EncryptBuff(pconn, pconn.sendBuff.Bytes())
+		// 	return len(buff), nil
+		// } else {
+		// 	pconn.sendBuff.Write(buff)
+		// 	EncryptBuff(pconn, pconn.sendBuff.Bytes()[pconn.sendBuff.Len()-length:])
+		// 	return length, nil
+		// }
+	} else {
+		/*如果数据长度大于 2048,先发送缓冲区中的数据,然后直接发送新的大数据块。*/
+		if length > 2048 {
+			if _, err := pConn.conn.Write(pConn.sendBuff.Bytes()); err != nil {
+				//将缓冲区重置为空
+				pConn.sendBuff.Reset()
+				return 0, err
+			}
+			//将缓冲区重置为空
+			pConn.sendBuff.Reset()
+			//发送新的大数据块
+			_, err := pConn.conn.Write(buff)
+			return length, err
+
+		} else if pConn.sendBuff.Len()+length >= 8*1024 {
+			//缓冲区空间不足:
+			/*- 如果当前缓冲区中的数据加上新数据的长度超过 `8 * 1024` 字节,
+			则先发送缓冲区中的数据,然后重置缓冲区,将新数据拷贝到缓冲区中。*/
+			if _, err := pConn.conn.Write(pConn.sendBuff.Bytes()); err != nil {
+				pConn.sendBuff.Reset()
+				return 0, err
+			}
+			pConn.sendBuff.Reset()
+			pConn.sendBuff.Write(buff)
+			return length, nil
+		} else {
+			//TODO: 这里c代码里是添加到缓存buff里面 ,不直接发送
+			pConn.sendBuff.Write(buff)
+			return length, nil
+		}
+	}
+}
+
+func rhSendInt32(pConn *xuguConn, i uint32) (int, error) {
+
+	// 创建一个 4 字节的缓冲区,用于存储网络字节序的 int32 值
+	var networkBytes [4]byte
+	// 将 int32 值转换为网络字节序(大端字节序)并写入缓冲区
+	binary.BigEndian.PutUint32(networkBytes[:], i)
+
+	//fmt.Println("networkBytes[] ", networkBytes[:])
+
+	return RhSend(pConn, networkBytes[:], 4)
+}
+
+func RhFlush(pConn *xuguConn) (int, error) {
+	if pConn.sendBuff.Len() > 0 {
+		if _, err := pConn.conn.Write(pConn.sendBuff.Bytes()); err != nil {
+			pConn.sendBuff.Reset()
+			return 0, err
+		}
+		pConn.sendBuff.Reset()
+	}
+	return 0, nil
+}

+ 172 - 0
xugu/xugu_stmt.go

@@ -0,0 +1,172 @@
+package xugu
+
+import (
+	"database/sql/driver"
+	"encoding/base64"
+	"encoding/binary"
+	"errors"
+	"fmt"
+	"unsafe"
+)
+
+type xuguStmt struct {
+
+	// 上下文连接句柄指针
+	stmt_conn *xuguConn
+
+	// 布尔值,用于标识执行的 SQL 语句是否已准备好
+	prepared bool
+
+	// 接受准备好的 SQL 语句的代码
+	prename []byte
+
+	// 布尔值,用于标识游标是否启用
+	curopend bool
+
+	// 游标名称
+	curname []byte
+
+	// 执行的 SQL 语句中的参数数量
+	param_count int
+	mysql       string
+
+	// 上下文结果集句柄指针
+	result unsafe.Pointer
+}
+
+/* Collect error information from the database server */
+func (self *xuguStmt) get_error() error {
+	// message := cgo_c_calloc(ERROR_BUFF_SIZE)
+	// defer func() {
+	// 	cgo_c_free(unsafe.Pointer(message))
+	// }()
+
+	// var length C.int
+	// cgo_xgc_error(&self.stmt_conn, message, &length)
+	// return errors.New(C.GoString(message))
+	return errors.New("xuguStmt error")
+}
+
+/* {{ */
+func (stmt *xuguStmt) Close() error {
+	fmt.Println("\n>>>>>> (stmt *xuguStmt) Close method called")
+	return nil
+}
+
+func (stmt *xuguStmt) NumInput() int {
+	fmt.Println("\n>>>>>NumInput method called")
+
+	parser := &xuguParse{
+		bind_type:   0,
+		param_count: 0,
+		position:    0,
+	}
+
+	fmt.Println("stmt.mysql: ", stmt.mysql)
+	return parser.assertParamCount(stmt.mysql)
+	//return 0
+}
+
+func (stmt *xuguStmt) Exec(args []driver.Value) (driver.Result, error) {
+	fmt.Println(">>>>>>>Exec method called")
+	result := &xuguResult{
+		affectedRows: 0,
+		insertId:     0,
+	}
+	//最终发送消息给服务器
+	XGC_Execute(stmt.stmt_conn, stmt.mysql)
+	//处理服务器返回
+	stmt.stmt_conn.conn.Read(stmt.stmt_conn.readBuff.buf)
+	readBuf := stmt.stmt_conn.readBuff
+	//fmt.Println("Message from server:", (buffer[:n]))
+	fmt.Println("Message from server:", string(stmt.stmt_conn.readBuff.buf))
+	//读取一字节
+	char := readBuf.peekChar()
+	switch char {
+	case '$':
+		fmt.Println("消息类型为$")
+	case 'I':
+		readBuf.idx++
+		fmt.Println("消息类型为I")
+		//Rowid_Len
+		Rowid_Len := binary.BigEndian.Uint32(readBuf.readNext(4))
+		fmt.Println("Rowid_Len", Rowid_Len)
+		//Rowid_Data	// Base64 解码
+		Rowid_Data, err := base64.StdEncoding.DecodeString(string(readBuf.readNext(int(Rowid_Len))))
+		if err != nil {
+			fmt.Println("Error decoding:", err)
+
+		}
+		fmt.Println("Rowid_Data: ", string(Rowid_Data))
+
+	case 'U':
+		fmt.Println("消息类型为U")
+	case 'D':
+		fmt.Println("消息类型为D")
+	case 'E':
+		fmt.Println("消息类型为E")
+	case 'W':
+		fmt.Println("消息类型为W")
+	case 'M':
+		fmt.Println("消息类型为M")
+	}
+
+	//return nil, errors.New("Exec not implemented")
+	return result, nil
+}
+
+func (stmt *xuguStmt) Query(args []driver.Value) (driver.Rows, error) {
+	fmt.Println("\n>>>>>>Query method called")
+	fmt.Println("stmt.mysql: ", stmt.mysql)
+	if switchSQLType(stmt.mysql) != SQL_SELECT {
+		return nil, errors.New("The executed SQL statement is not a SELECT")
+	}
+	if !stmt.prepared {
+		return nil, errors.New("SQL statement is not Prepared")
+	}
+
+	parser := &xuguParse{
+		bind_type:   0,
+		param_count: 0,
+		position:    0,
+	}
+
+	if len(args) != 0 {
+		for pos, param := range args {
+			err := parser.assertParamType(param, pos)
+			if err != nil {
+				return nil, err
+			}
+		}
+
+	}
+	// if len(parser.Val) != parser.assertParamCount(stmt.mysql) {
+	// 	return nil, errors.New("The number of parameters does not match")
+	// }
+
+	//最终发送消息给服务器
+	XGC_Execute(stmt.stmt_conn, stmt.mysql)
+
+	//----------------处理服务器查询返回
+
+	stmt.stmt_conn.conn.Read(stmt.stmt_conn.readBuff.buf)
+
+	//fmt.Println("Message from server:", (buffer[:n]))
+	fmt.Println("Message from server:", string(stmt.stmt_conn.readBuff.buf))
+	//fmt.Println("\nMessage from server2:", (stmt.stmt_conn.readBuff.buf))
+	results, err := parseSelectResult(stmt.stmt_conn.readBuff)
+	if err != nil {
+		fmt.Println("parseSelectResult parseSelectResult err", err.Error())
+		return nil, err
+	}
+
+	fmt.Println("results:", results)
+
+	a := &xuguRows{
+		results:   results,
+		prepared:  stmt.prepared,
+		rows_conn: stmt.stmt_conn,
+	}
+	fmt.Println("\n>>>>>>stmt Query  end==============================================================")
+	return a, nil
+}

+ 53 - 0
xugu/xugu_test.go

@@ -0,0 +1,53 @@
+package xugu
+
+import (
+	"fmt"
+	"net"
+	"os"
+	"testing"
+)
+
+func GetConn() (xgConn *xuguConn) {
+	conn, err := net.Dial("tcp", "10.28.20.101:5190")
+	if err != nil {
+		fmt.Println("Error connecting:", err)
+		os.Exit(1)
+	}
+
+	// 发送消息到服务器
+	message := "login   database = 'SYSTEM' user = 'SYSDBA'  password = 'SYSDBA' version='201' "
+	_, err = conn.Write([]byte(message))
+	if err != nil {
+		fmt.Println("Error writing to server:", err)
+		return nil
+	}
+
+	fmt.Println("Message sent:", message)
+
+	// 接收服务器的返回消息
+	buffer := make([]byte, 1024)
+	n, err := conn.Read(buffer)
+	if err != nil {
+		fmt.Println("Error reading from server:", err)
+		return nil
+	}
+	fmt.Println("Message from server:", string(buffer[:n]))
+	xgConn = &xuguConn{conn: conn}
+	return
+}
+func TestXugu_GetConnection(t *testing.T) {
+	xgConn := GetConn()
+	SockSendCommand0(xgConn, "select * from gotest")
+	///	parseResponse(conn)
+	// 接收服务器的返回消息
+	buffer := make([]byte, 1024)
+	n, err := xgConn.conn.Read(buffer)
+	if err != nil {
+		fmt.Println("Error reading from server:", err)
+		return
+	}
+	//fmt.Println("Message from server:", (buffer[:n]))
+	fmt.Println("Message from server:", string(buffer[:n]))
+	fmt.Println("end")
+
+}

+ 221 - 0
xugu/xugu_utils.go

@@ -0,0 +1,221 @@
+package xugu
+
+import (
+	"fmt"
+	"strings"
+)
+
+func parseDSN(dsn string) dsnConfig {
+	// Initialize a dsnConfig struct
+	var config dsnConfig
+
+	// Split the string by semicolons
+	pairs := strings.Split(dsn, ";")
+
+	// Iterate over the pairs and map them to the struct fields
+	for _, pair := range pairs {
+		// Split each pair by the equals sign
+		kv := strings.SplitN(pair, "=", 2)
+		if len(kv) != 2 {
+			continue
+		}
+		key, value := strings.TrimSpace(kv[0]), strings.Trim(strings.TrimSpace(kv[1]), "'")
+		keyL := strings.ToLower(key)
+
+		// Map the key to the appropriate struct field
+		switch keyL {
+		case "ip":
+			config.IP = value
+		case "port":
+			config.Port = value
+		case "db":
+			config.Database = value
+		case "user":
+			config.User = value
+		case "pwd":
+			config.Password = value
+		case "encryptor":
+			config.Encryptor = value
+		case "char_set":
+			config.CharSet = value
+		case "time_zone":
+			config.TimeZone = value
+		case "iso_level":
+			config.IsoLevel = value
+		case "lock_timeout":
+			config.LockTimeout = value
+		case "auto_commit":
+			config.AutoCommit = value
+		case "strict_commit":
+			config.StrictCommit = value
+		case "result":
+			config.Result = value
+		case "return_schema":
+			config.ReturnSchema = value
+		case "return_cursor_id":
+			config.ReturnCursorID = value
+		case "lob_ret":
+			config.LobRet = value
+		case "return_rowid":
+			config.ReturnRowid = value
+		case "version":
+			config.Version = value
+		}
+	}
+
+	return config
+}
+
+func generateLoginString(config dsnConfig) string {
+	baseString := "login   database = '%s' user = '%s'  password = '%s' "
+	additionalParams := ""
+
+	if config.Encryptor != "" {
+		additionalParams += fmt.Sprintf(" encryptor='%s'", config.Encryptor)
+	}
+	if config.CharSet != "" {
+		additionalParams += fmt.Sprintf(" char_set='%s'", config.CharSet)
+	}
+	if config.TimeZone != "" {
+		additionalParams += fmt.Sprintf(" time_zone='%s'", config.TimeZone)
+	}
+	if config.IsoLevel != "" {
+		additionalParams += fmt.Sprintf(" iso_level='%s'", config.IsoLevel)
+	}
+	if config.LockTimeout != "" {
+		additionalParams += fmt.Sprintf(" lock_timeout='%s'", config.LockTimeout)
+	}
+	if config.AutoCommit != "" {
+		additionalParams += fmt.Sprintf(" auto_commit='%s'", config.AutoCommit)
+	}
+	if config.StrictCommit != "" {
+		additionalParams += fmt.Sprintf(" strict_commit='%s'", config.StrictCommit)
+	}
+	if config.Result != "" {
+		additionalParams += fmt.Sprintf(" result='%s'", config.Result)
+	}
+	if config.ReturnSchema != "" {
+		additionalParams += fmt.Sprintf(" return_schema='%s'", config.ReturnSchema)
+	}
+	if config.ReturnCursorID != "" {
+		additionalParams += fmt.Sprintf(" return_cursor_id='%s'", config.ReturnCursorID)
+	}
+	if config.LobRet != "" {
+		additionalParams += fmt.Sprintf(" lob_ret='%s'", config.LobRet)
+	}
+	if config.ReturnRowid != "" {
+		additionalParams += fmt.Sprintf(" return_rowid='%s'", config.ReturnRowid)
+	}
+	if config.Version != "" {
+		additionalParams += fmt.Sprintf(" version='%s'", config.Version)
+	} else {
+		additionalParams += " version='201'"
+	}
+
+	finalString := fmt.Sprintf(baseString, config.Database, config.User, config.Password)
+	if additionalParams != "" {
+		finalString += additionalParams
+	}
+	//finalString += " version='201'"
+	return finalString
+}
+
+// cgo_xgc_sql_type -> fun_sql_type
+// 判断sql类型
+func switchSQLType(sql string) int {
+	// 去掉首尾的空格、换行符和回车符
+	sql = strings.TrimSpace(sql)
+	if len(sql) < 6 {
+		return SQL_OTHER
+	}
+
+	// 取前6个字符并转为大写
+	kstr := strings.ToUpper(sql[:6])
+
+	// 根据SQL语句前缀判断类型
+	switch {
+	case strings.HasPrefix(kstr, "SELECT"):
+		if strings.Contains(sql, ";") && len(sql[strings.Index(sql, ";"):]) > 5 {
+			return SQL_OTHER // 多结果集
+		}
+		return SQL_SELECT
+	case strings.HasPrefix(kstr, "INSERT"):
+		return SQL_INSERT
+	case strings.HasPrefix(kstr, "UPDATE"):
+		return SQL_UPDATE
+	case strings.HasPrefix(kstr, "DELETE"):
+		return SQL_DELETE
+	case strings.HasPrefix(kstr, "CREATE"):
+		return SQL_CREATE
+	case strings.HasPrefix(kstr, "ALTER "):
+		return SQL_ALTER
+	case strings.HasPrefix(kstr, "EXEC "):
+		return SQL_PROCEDURE
+	case strings.HasPrefix(kstr, "EXECUT"):
+		return SQL_PROCEDURE
+	case strings.HasPrefix(kstr, "STC"):
+		return SQL_SELECT
+	default:
+		return SQL_OTHER
+	}
+}
+
+func getSQLCType(typeID uint32) int32 {
+	switch typeID {
+	case TYPE_BOOL:
+		return XG_C_BOOL
+	case TYPE_I1:
+		return XG_C_TINYINT
+	case TYPE_I2:
+		return XG_C_SHORT
+	case TYPE_I4:
+		return XG_C_INTEGER
+	case TYPE_I8:
+		return XG_C_BIGINT
+	case TYPE_R4:
+		return XG_C_FLOAT
+	case TYPE_R8:
+		return XG_C_DOUBLE
+	case TYPE_NUM:
+		return XG_C_NUMERIC
+	case TYPE_DATETIME:
+		return XG_C_DATETIME
+	case TYPE_DATETIME_TZ:
+		return XG_C_DATETIME_TZ
+	case TYPE_INTERVAL_Y2M:
+		return XG_C_INTERVAL_YEAR_TO_MONTH
+	case TYPE_INTERVAL_D2S:
+		return XG_C_INTERVAL_DAY_TO_SECOND
+	case TYPE_GUID:
+		return TYPE_GUID + 40
+	case TYPE_TIME_TZ:
+		return XG_C_TIME_TZ
+	case TYPE_DATE:
+		return XG_C_DATE
+	case TYPE_TIME:
+		return XG_C_TIME
+	case TYPE_BINARY:
+		return XG_C_BINARY
+	case TYPE_CLOB:
+		return XG_C_CLOB
+	case TYPE_BLOB:
+		return XG_C_BLOB
+	case TYPE_INTERVAL_Y, TYPE_INTERVAL_M, TYPE_INTERVAL_D, TYPE_INTERVAL_H,
+		TYPE_INTERVAL_MI, TYPE_INTERVAL_S, TYPE_INTERVAL_D2H, TYPE_INTERVAL_D2M,
+		TYPE_INTERVAL_H2M, TYPE_INTERVAL_H2S, TYPE_INTERVAL_M2S:
+		return XG_C_INTERVAL
+	default:
+		return XG_C_CHAR
+	}
+}
+
+func needCopyV(xgtype int) bool {
+	switch xgtype {
+	case XG_C_DOUBLE, XG_C_FLOAT, DATETIME_ASLONG, XG_C_BIGINT, XG_C_INTEGER, XG_C_SHORT, XG_C_TINYINT, XG_C_CHARN1, XG_C_BOOL, XG_C_NUMERIC:
+		return true
+
+	default:
+		return false
+	}
+
+}

+ 311 - 0
协议.ini

@@ -0,0 +1,311 @@
+http://gitlab2.xugu.com/cyj/xugusql
+
+1.连接:
+     1.1 客户端主动发起连接,创建socket后,向服务端发出连接字符串: 
+	login   database = '数据库名' user = '用户名'  password = '口令' [ encryptor = '加密库的解密口令' ]  
+		[ char_set = '客户端使用的字符集名'] [ time_zone='时区']  [iso_level='事务隔离级别']
+		[ lock_timeout='加锁超时' ] [ auto_commit='on或off']  [ strict_commit='on或off'] 
+		[ result='char']   [ return_schema='on或off ] [ return_cursor_id='on或off ]
+		[ lob_ret='descriptor']  [ return_rowid = true] version='客户端协议版本号'
+		
+	解释:	
+		连接串中除了login database=数据库名 user = '用户名'  password = '口令' 外,其余为可选项,若未指定那些项,则系统为连接设置默认值
+		
+		数据解密口令:用于启动加密数据库,若库未加密,则可不要,但带上此参数不会影响连接
+		
+		客户端使用的字符集名: 'GB2312' ,'GBK','BIG5' 等,默认为GBK
+		
+		时区:	'GMT +08:00'
+		
+		事务隔离级别:
+			'READ COMMITED':读已提交
+			'REPEATIABLE READ':可重复读 
+			'SERIALIZABLE':序列化
+		
+		加锁超时:
+			毫秒数,表示本连接中的事务在争用锁时,最多等候的时间,若在此时间内加锁不成功,则回滚事务并报错
+		
+		auto_commit:
+			on:表示自动提交,即每个命令都作为一个事务,命令完成立即提交
+			off:非自动提交,需要 commit命令才提交
+		
+		strict_commit: 是否该连接中执行的事务使用严格提交(即须记实了事务日志才向客户端返回'K')
+		
+		result='char': 当设置了该项后,则float,integer等不以字串方式返回的结果皆强制使用字符串方式返回(此项对于odbc,jdbc等标准驱动皆不使用)
+		
+		return_schema: 是否在返回字段名信息时加上模式名,若设置为'on',则返回的字段名形式为: 模式名.表名.字段名 '%' 别名,否则返回的字段名形式为:表名.字段名 '%' 别名
+		
+		return_cursor_id: 若该项设置为'on',则存贮过程的ref_cursor的返回值为cursor的ID号,此时,客户端若要使用ref_currsor的结果,则应使用fetch命令,cursor的名为: 'CurN' + 游标号的16进制字串,
+				若该项设置为off或没有该项,则将在返回结果中直接嵌入ref_cursor的结果集 
+		
+		lob_ret='descriptor': 该项目前服务器版本中没有,拟新加入,若有该项,则在返回大对象时不以数据形式返回,代之以描述符,当真正需要大对象数据时,使用 GET LOB 大对象描述符;命令从服务器取回数据
+		
+		version:
+			客户端协议版本,目前都应是:'201'
+
+ 
+    1.2 服务器端: 发回字节'K'表示成功,否则,发回错误信息(见错误信息格式)并立即关闭连接
+
+
+2.所有DML及DDL访问:
+
+    2.1 客户端发出访问: 
+	
+        QryStream:  '?'  Comand_Len   Comand_str   Param_num   { Param_name_len Param_name  Param_DType Param_Data_Len Param_Data }*
+		
+	解释:
+		Command_len: 	命令长度(网络序32位整数,计算命令长度时不包括结束符0x0)
+		Command_str: 	命令字串(发送时应包括结束符0x0)	  
+		Param_num:	参数个数(网络序16位整数,若没有参数则此值为0)
+		
+		Param_name_len: 参数名字长度(网络序短整数,若参数无名,则此值为0)
+		Param_name:	参数名(该部分字节数应与Param_name_Len一致)
+		
+		参数输出输入类型
+		Param_DType:	参数数据类型号(网络序16位整数)
+		Param_Data_Len:	参数长度(网络序32位整数)
+		Param_Data:	参数数据(该部分字节数应与Param_Data_Len一致)
+		
+		{}*:		表示参数部分可有0至多个参数,其重复的次数取决于Param_num,若Param_num为0,则没有参数需要发送
+ 		
+
+    2.2 服务器返回:  服务器可返回一组或多组信息,并以字节'K'标识所有信息已传送完毕
+	
+	SrvResponse:     { FormArgDescri | SelectResult | InsertResult | UpdateResult | DeleteResult | ProcRet | OutParamRet | ErrInfo | WarnInfo | Message }*  'K'
+	
+	FormArgDescri:   '$' Arg_Num { Arg_Name_Len Arg_Name Arg_No Arg_DType Arg_Preci_Scale }+ 
+	
+	SelectResult:    'A' Field_Num { Field_Name_Len Field_Name Field_DType Field_Preci_Scale Field_Flag }+  { 'R' Col_len1 Col_Data1 ... }*
+	
+	InsertResult:	 'I' Rowid_Len  Rowid_Data
+	
+	UpdateResult:	 'U' Update_Num	
+
+	DeleteResult:	 'D' Delete_Num
+
+	ProcRet:	 'O' Ret_DType  Ret_Data_Len  Ret_Data
+
+	OutParamRet:	 'P' Out_Param_No   Out_Param_DType  Out_Param_Len  Out_Param_Data
+
+	ErrInfo:	 'E' Err_Str_Len  Err_Str
+	
+	WarnInfo:	 'W' Warn_Str_Len Warn_Str
+
+	Message:	 'M' Msg_Str_Len  Msg_Str
+
+
+	解释:
+		FormArgDescri:   形式参数描述,执行prepare语句后,通常返回此部分信息
+		Arg_Num:	 形参个数(网络序32位整数)
+		Arg_Name_Len:	 形参名长度(网络序32位整数)
+		Arg_Name:	 形参名(字节数与Arg_Name_Len一致)
+		Arg_No:		 形参的序号(网络序32位整数,若prepare语句中使用?号表示参数,则服务器按?号的顺序从1开始为其设定序号)
+		Arg_DType:	 形参的数据类型ID(网络序32位整数)
+		Arg_Preci_Scale: 形参的精度标度(网络序32位整数
+
+		SelectResult:	 查询结果集,包含一组结果集字段描述 以及0至多个行数据
+		Field_Num:	 结果集字段数
+		Field_Name_Len:  字段名长度(网络序32位整数)
+		Field_Name:	 字段名(字节数与Field_Name_Len一致)
+		Field_DType:	 字段数据类型ID(网络序32位整数)
+		Field_Preci_Scale:字段的精度标度(网络序32位整数,若为-1或0表示没有精度标度信息,否则高16位为精度低16位为标度)
+		Field_Flag:	字段的其它属性标志(网络序32位整数),其中每一个位表征字段的某一个特性,各位对应的特性如下
+				FIELD_BASE_TAB=1,	/* 表示字段是实际的表的字段(否则,表示字段实际上是表达式) */
+				FIELD_NOT_NULL=2,	/* 表示字段具有非空约束 */
+				FIELD_IS_PRIMARY=4, 	/* 表示字段是主键 */
+				FIELD_IS_SERIAL=8,	/* 表示字段是序列值 */
+				FIELD_IS_TIMESTAMP=16,	/* 表示字段是timestamp */
+				FIELD_IS_LOB=32,	/* 表示字段是大对象类型 */
+				FIELD_IS_UNIQUE=64,	/* 表示字段是唯一值类型 */
+				FIELD_IS_ROWID=128,	/* 表示字段是rowid */
+				FIELD_IS_DUMMY=256, 	/* 表示字段不应输出 */
+				FIELD_IS_HIDE=512,	/* 表示字段是隐藏字段 */				 
+
+		Col_len:	 列数据长度(网络序32位整数)
+		Col_Data:	 列数据(字节数与Col_Len一致)
+		
+		注:  返回结果中,记录数据由 'R'开头,后跟若干个列的长度及数据,每个'R'后的列数据个数为Field_Num个		
+
+
+		InsertResult:	插入语句的返回结果
+		Rowid_Len:	插入语句返回的新行的ROWID数据的长度
+		Rowid_Data:	插入语句返回的新行的ROWID数据(为base64编码)
+
+
+		UpdateResult:	更改语句返回的结果
+		Update_Num:	更改语句影响的行数(网络序32位整数)
+		
+		DeleteResult:	删除语句的返回结果
+		Delete_Num:	删除的行数
+		
+		ProcRet:	存贮函数调用的返回值
+		Ret_DType:	返回值数据类型(网络序32位整数)
+		Ret_Data_Len:	返回值数据长度(网络序32位整数)
+		Ret_Data:	返回值数据	
+
+		OutParamRet:	返回型参数
+		Out_Param_No:	返回型参数的参数号
+		Out_Param_DType:返回参数的数据类型(网络序32位整数)
+		Out_Param_Len:	返回参数的数据长度(网络序32位整数)
+		Out_Param_Date:	返回参数的数据	
+
+		ErrInfo:	错误信息(通常在返回数据的最后出现,即出现在 'K'字符前)
+		Err_Str_Len:	错误信息长度(网络序32位整数)
+		Err_Str:	错误信息(字节数与Err_Str_Len一致)
+	
+		WarnInfo:	警告信息(通常在返回数据的最后出现,即出现在 'K'字符前)
+		Warn_Str_Len:	警告信息长度(网络序32位整数)
+		Warn_Str:	警告信息(字节数与Warn_Str_Len一致)
+
+		Message:	通知信息(出现的位置没有一定的规定)
+		Msg_Str_Len:	通知信息长度(网络序32位整数)
+		Msg_Str:	通知信息(字节数与Msg_Str_Len一致)
+
+		注: {}* 中各种返回信息是并列的关系,可能一次返回中含有0个至多个返回信息,不能假定改到其中其种返回信息就认为没有其它种类的返回信息,
+			有些类型的返回信息甚至可能重重出现,如:一次发多条select 语句就会造成返回信息中包含多个查询结果集.
+
+	2.3 关于新版本中将增加的大对象数据返回形式的说明
+		
+		a) 当在连接中未加入了 lob_ret='descriptor'子串时,使用原来的方式返回大对象数据
+
+		b) 当在连接中加入了 lob_ret='descriptor'子串时,应该将大对象对应的列返回值视作描述符,描述符分两种:一种为内嵌在行内的大对象数据的描述符,一种为非内嵌大对象描述符
+			内嵌大对象描述符:   
+				BLOB类型:'b' + 大对象数据  即:大对象数据仍直接返回,只是前面加了一个 'b'字符,因此大对象数据的长度实际为列数据长度-1
+				CLOB类型:'c' + 大对象数据  即:大对象数据仍直接返回,只是前面加了一个 'c'字符,因此大对象数据的长度实际为列数据长度-1
+
+			非内嵌大对象描述符:
+				BLOB类型: 'B' + 大对象描述符字串 ,该字符串由两部分组成:前面为descriptor的base64编码,后面为大对象的数据长度(10进制数值串),
+					   中间由空格分开;整个串的长度为列数据长度-1;
+				CLOB类型: 'C' + 大对象描述符字串,其余与BLOB相同 
+		
+		c) 当大对象字段返回的字段长度为0时,仍表示该字段为空值,不能再去取该字段的数据作为描述符用
+
+		d) 对于内嵌大对象,驱动应保存其数据,而不能从服务器另取数据
+
+		e) 对于非内嵌大对象,驱动在向应用程序返回数据时使用: 
+			GET LOB '大对象描述符' offset 偏移量 limit 读取长度; 命令来读取大对象数据. (注意:大对象描述符串包括前面的‘B’或‘C’,
+				但不包含后面的大对象长度字串,描述前后有'号)  
+			服务端以单行单列结果集的形式返回数据:
+				对于BLOB,返回的字段名为:BLOBDATA,返回记录的数据长度为实际读取长度(通常就是limit,若已读到尾,则小于limit),
+				对于CLOB,返回的字段名为:CLOBDATA,若不存在库字符集与客户端字符集的差异,则返回记录的数据长度为实际读取长度,
+				否则,返回的数据长度为字符集转换后的长度,当然,内容也就为转换后的内容。
+
+				
+	
+3.数据类型ID的取值:以下取值未写明数值的按有数值的依次向下增1
+	
+	TYPE_EMPTY = 0,
+	TYPE_NULL,
+	TYPE_BOOL,
+	TYPE_I1,
+	TYPE_UI1,
+	TYPE_I2,
+	TYPE_UI2,
+	TYPE_I4,
+	TYPE_UI4,
+	TYPE_I8,
+	TYPE_UI8,
+	TYPE_R4,
+	TYPE_R8,
+	TYPE_DATE,
+	TYPE_TIME,
+	TYPE_DATETIME,
+	TYPE_TIMESTAMP,
+	TYPE_INTERVAL_D2S,
+	TYPE_SMALLTIME,
+	TYPE_TIME_TZ,
+	TYPE_DATETIME_TZ,
+	TYPE_INTERVAL_Y2M,
+
+	TYPE_DECIMAL,
+	TYPE_NUMERIC,
+	TYPE_CY, 
+	TYPE_STR,
+	TYPE_CHAR,
+	TYPE_VARCHAR,
+	TYPE_CLOB,
+	TYPE_BINARY,
+	TYPE_BLOB,
+	TYPE_IMAGE,
+	TYPE_BLOB_INROW,
+	TYPE_BLOB_S,
+	TYPE_BLOB_M,
+	TYPE_BLOB_OM,
+	TYPE_STREAM,
+	TYPE_AUDIO,
+	TYPE_VIDEO,
+	TYPE_ROWVER,
+
+	TYPE_INTERVAL_Y,
+	TYPE_INTERVAL_M,
+	TYPE_INTERVAL_D,
+	TYPE_INTERVAL_H,
+	TYPE_INTERVAL_MI,
+	TYPE_INTERVAL_S,
+	TYPE_INTERVAL_D2H,
+	TYPE_INTERVAL_D2M,
+	TYPE_INTERVAL_H2M,
+	TYPE_INTERVAL_H2S,
+	TYPE_INTERVAL_M2S,
+	TYPE_GUID,
+
+	TYPE_GEOMETRY=60,
+	TYPE_POINT,
+	TYPE_BOX,
+	TYPE_POLYLINE,
+	TYPE_POLYGON,
+	
+
+	TYPE_MAX_SYS=100,
+
+	TYPE_BLADE_BEGIN=101,
+	TYPE_BLADE_END=1000,
+	
+	TYPE_OBJECT=1001,		/* 对象类型 */
+	TYPE_RECORD,			/* 记录类型 */
+	TYPE_VARRAY,			/* 数组类型 */
+	TYPE_TABLE,			/* 表类型 */
+	TYPE_IDXBY_TAB,			/* index by 表类型 */
+	TYPE_ROWTYPE,			/* 引用行类型 */
+	TYPE_COLTYPE,			/* 引用列类型 */
+	TYPE_ROW,			/* 行类型  */
+	TYPE_CUR_REC,			/* 引用记录类型(不能更改字段) */
+	TYPE_CURSOR,			/* 游标类型 */
+	TYPE_REFCUR,			/* REF_CURSOR类型 */
+	TYPE_PARAM,
+
+	TYPE_UDT_BEGIN=1100,	
+
+
+4.数据类型返回格式
+	2.1.若连接时连接串中有 result='char',则各类型数据的返回值皆转换成字符串的形式进行返回,
+	    
+	    	数值型(integer,float,numeric等),返回的字符串变由数字与小数点组成
+	    	日期型格式:‘2011-3-3’
+		时间型格式 ‘12:03:40’或 ‘12:03:40.999'
+		日期时间型格式:‘2011-3-3 12:34:23' 或 ‘2011-3-3 12:34:23.999'
+		带时区的日期时间格式:‘2011-3-3 12:34:23+08:00' 或‘2011-3-3 12:34:23-08:00'
+		带时区的时间格式:‘12:34:23+08:00' 或‘12:34:23-08:00'
+
+	2.2若连接串中不含 result='char',则各类型数据的返回值通常以二进制方式返回
+		
+		char、varchar、clob、numeric等类型以字符串形式返回
+		数值型(integer等除numeric外)返回格式,就是其二进制格式,只是转换成了网络序
+		date: 32位网络序整数,为从1970-1-1起流失的天数(负数表示在1970-1-1之前)
+		time: 32位网络序整数,为一天内从0:0:0开始流失的毫秒数
+		datetime:64位网络序整数,为从1970-1-1起流失的毫秒数(负数表示在1970-1-1之前)
+		time with time zone:6字节,前4字节为32位网络序时间值,后2字节为16位网络序时区值,时间值与time的处理方式一样,
+			时区值为时区相对于格林威治天文台的时差,单位为分钟,有正负之分。
+		datetime with time zone:10字节,前8字节为64位网络序日期时间值,后2字节为16位网络序时区值,日期时间值与datetime的处理方式一样,
+			时区值为时区相对于格林威治天文台的时差,单位为分钟,有正负之分。
+
+		interval year:	32位网络序整数,单位为年
+		interval month:	32位网络序整数,单位为月
+		interval day:	32位网络序整数,单位为天
+
+		interval hour:	32位网络序整数,单位为小时
+		interval minute:32位网络序整数,单位为分钟
+		interval second:64位网络序整数,单位为毫秒(微秒  CYJ)
+
+		注:interval XX to YY(如interval day to second)其返回的值的格式与 interval YY相同,即:interval day to second 返回格式与interval second相同,余类推
+		

+ 83 - 0
参数映射失败.go

@@ -0,0 +1,83 @@
+func (rs *Rows) nextLocked() (doClose, ok bool) {
+	if rs.closed {
+		return false, false
+	}
+
+	// Lock the driver connection before calling the driver interface
+	// rowsi to prevent a Tx from rolling back the connection at the same time.
+	rs.dc.Lock()
+	defer rs.dc.Unlock()
+
+	if rs.lastcols == nil {
+		rs.lastcols = make([]driver.Value, len(rs.rowsi.Columns()))
+	}
+
+	rs.lasterr = rs.rowsi.Next(rs.lastcols)
+
+
+-------
+
+func (rs *Rows) Next() bool {
+	// If the user's calling Next, they're done with their previous row's Scan
+	// results (any RawBytes memory), so we can release the read lock that would
+	// be preventing awaitDone from calling close.
+	rs.closemuRUnlockIfHeldByScan()
+
+	if rs.contextDone.Load() != nil {
+		return false
+	}
+
+	var doClose, ok bool
+	withLock(rs.closemu.RLocker(), func() {
+		doClose, ok = rs.nextLocked()
+	})
+
+
+	----------
+	func (rs *Rows) Scan(dest ...any) error {
+		fmt.Printf("驱动内部的 sCan %#v \n", dest)
+		if rs.closemuScanHold {
+			// This should only be possible if the user calls Scan twice in a row
+			// without calling Next.
+			return fmt.Errorf("sql: Scan called without calling Next (closemuScanHold)")
+		}
+		rs.closemu.RLock()
+	
+		if rs.lasterr != nil && rs.lasterr != io.EOF {
+			rs.closemu.RUnlock()
+			return rs.lasterr
+		}
+		if rs.closed {
+			err := rs.lasterrOrErrLocked(errRowsClosed)
+			rs.closemu.RUnlock()
+			return err
+		}
+	
+		if scanArgsContainRawBytes(dest) {
+			rs.closemuScanHold = true
+		} else {
+			rs.closemu.RUnlock()
+		}
+	
+		if rs.lastcols == nil {
+			rs.closemuRUnlockIfHeldByScan()
+			return errors.New("sql: Scan called without calling Next")
+		}
+		if len(dest) != len(rs.lastcols) {
+			fmt.Println(" len(rs.lastcols)", len(rs.lastcols))
+			fmt.Println(" len(dest) ", len(dest), dest)
+			fmt.Println("列数据不匹配")
+	
+			rs.closemuRUnlockIfHeldByScan()
+			return fmt.Errorf("sql: expected %d destination arguments in Scan, not %d", len(rs.lastcols), len(dest))
+		}
+		fmt.Println("列数据匹配")
+		for i, sv := range rs.lastcols {
+			err := convertAssignRows(dest[i], sv, rs)
+			if err != nil {
+				rs.closemuRUnlockIfHeldByScan()
+				return fmt.Errorf(`sql: Scan error on column index %d, name %q: %w`, i, rs.rowsi.Columns()[i], err)
+			}
+		}
+		return nil
+	}