GTong 1 vuosi sitten
vanhempi
säilyke
15c6fd7ce9

+ 3 - 0
.vs/ProjectSettings.json

@@ -0,0 +1,3 @@
+{
+  "CurrentProjectSetting": null
+}

+ 6 - 0
.vs/VSWorkspaceState.json

@@ -0,0 +1,6 @@
+{
+  "ExpandedNodes": [
+    ""
+  ],
+  "PreviewInSolutionExplorer": false
+}

BIN
.vs/slnx.sqlite


BIN
.vs/xg_driver/FileContentIndex/3dc7871b-88af-44df-8f3a-cacac4e32255.vsidx


BIN
.vs/xg_driver/v17/.wsuo


+ 12 - 0
.vs/xg_driver/v17/DocumentLayout.json

@@ -0,0 +1,12 @@
+{
+  "Version": 1,
+  "WorkspaceRootPath": "C:\\Program_GT\\Code\\Go\\Work\\xugu\\xg_driver\\",
+  "Documents": [],
+  "DocumentGroupContainers": [
+    {
+      "Orientation": 0,
+      "VerticalTabListWidth": 256,
+      "DocumentGroups": []
+    }
+  ]
+}

+ 1 - 1
main.go

@@ -14,7 +14,7 @@ func main() {
 	// ctx := context.Background()
 	// conn := xugu.NewConnector("127.0.0.1:5138")
 	// conn.Connect(ctx)
-	db, err := sql.Open("xugusql", "127.0.0.1:5138")
+	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);")
 	if err != nil {
 		log.Fatal(err)

+ 8 - 0
xg_driver.code-workspace

@@ -0,0 +1,8 @@
+{
+	"folders": [
+		{
+			"path": "."
+		}
+	],
+	"settings": {}
+}

+ 43 - 0
xugu/param.go

@@ -0,0 +1,43 @@
+package xugu
+
+import "unsafe"
+
+// 定义 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                    // 智能结果集
+)
+
+// 定义 XGCSParam 结构体
+type XGCSParam struct {
+	Type           HANDLE_TYPE       // 句柄类型
+	ParamNum       uint32            // 单行参数数量
+	PResourceNum   int               // 参数名称资源数量,步长128
+	ParamArraySize int               // 参数数组长度(数组大小)
+	Valuep         ***unsafe.Pointer // 实际参数指针数组,指向参数地址
+	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 由服务器提供的准备语句获取
+}
+
+const (
+	XG_ERROR        = -1
+	XG_NET_ERROR    = -4
+	XG_INVALID_ARG  = -3
+	XG_SOCKET_ERROR = -8
+	XG_LOGIN_ERROR  = -9
+)

+ 106 - 0
xugu/sema.go

@@ -0,0 +1,106 @@
+package xugu
+
+import (
+	"sync"
+	"time"
+)
+
+/*
+定义信号量结构体:Sema 结构体包含一个 sync.Mutex 和一个 sync.Cond,以及一个计数器 count。
+初始化信号量:initSema 和 initSemaN 函数用于初始化信号量,并设置信号量初始值。
+减少信号量(等待):decSema 函数用于减少信号量,当信号量为 0 时阻塞。
+增加信号量:incSema 函数用于增加信号量,并通知等待的 goroutine。
+等待信号量:waitSema 函数调用 decSema 实现等待。
+带超时的等待信号量:waitSemaT 函数实现带超时的等待信号量操作。
+清除信号量:clrSema 函数将信号量计数器置为 0。
+关闭信号量:在 Go 中不需要显式销毁信号量。
+模拟 usleep 函数:sleep 函数使用 time.Sleep 模拟 usleep。
+*/
+
+// 定义信号量结构体
+type Sema struct {
+	mutex sync.Mutex
+	cond  *sync.Cond
+	count int
+}
+
+// 初始化信号量
+func initSema() *Sema {
+	sema := &Sema{}
+	sema.cond = sync.NewCond(&sema.mutex)
+	return sema
+}
+
+// 初始化信号量,并设置信号量初始值
+func initSemaN(n int) *Sema {
+	sema := &Sema{count: n}
+	sema.cond = sync.NewCond(&sema.mutex)
+	return sema
+}
+
+// 减少信号量(等待)
+func (s *Sema) decSema() {
+	s.mutex.Lock()
+	for s.count == 0 {
+		s.cond.Wait()
+	}
+	s.count--
+	s.mutex.Unlock()
+}
+
+// 增加信号量
+func (s *Sema) incSema() {
+	s.mutex.Lock()
+	s.count++
+	s.cond.Signal()
+	s.mutex.Unlock()
+}
+
+// 等待信号量
+func (s *Sema) waitSema() {
+	s.decSema()
+}
+
+// 带超时的等待信号量
+func (s *Sema) waitSemaT(timeout time.Duration) bool {
+	s.mutex.Lock()
+	defer s.mutex.Unlock()
+	timer := time.NewTimer(timeout)
+	defer timer.Stop()
+	for s.count == 0 {
+		timer.Reset(timeout)
+		select {
+		case <-timer.C:
+			return false
+		case <-func() chan struct{} {
+			ch := make(chan struct{})
+			go func() {
+				s.cond.Wait()
+				close(ch)
+			}()
+			return ch
+		}():
+		}
+	}
+	s.count--
+	return true
+}
+
+// 清除信号量
+func (s *Sema) clrSema() {
+	s.mutex.Lock()
+	for s.count > 0 {
+		s.count--
+	}
+	s.mutex.Unlock()
+}
+
+// 关闭信号量(销毁信号量)
+func (s *Sema) closeSema() {
+	// 在 Go 中不需要显式销毁信号量
+}
+
+// 模拟 usleep 函数
+func sleep(t int) {
+	time.Sleep(time.Duration(t) * time.Millisecond)
+}

+ 111 - 0
xugu/xugu_lob.go

@@ -0,0 +1,111 @@
+package xugu
+
+import "errors"
+
+// LobDataItem 结构体定义
+type LobDataItem struct {
+	ChunkNo  int
+	ItemData []byte // 使用 interface{} 表示任意类型的数据
+	Next     *LobDataItem
+}
+
+// LOB_PICE 常量定义
+const LOB_PICE = 1024 * 1024 // 1 MB
+
+// XuguLob 结构体定义
+type XuguLob struct {
+	Type      HANDLE_TYPE
+	ChunkNum  int
+	State     int // 0 inited  1 inputing  2 inputed ok
+	DataOff   int64
+	RPos      int64 // 增加于 2017-09-08
+	CurItem   *LobDataItem
+	HeadItem  *LobDataItem
+	RdIfValue interface{}
+	Desc      string // 增加于 2017-09-12
+	ErrStr    string
+}
+
+func CreateLob() XuguLob {
+	return XuguLob{
+		Type:      HT_LOB,
+		ChunkNum:  0,
+		State:     0,
+		DataOff:   0,
+		RPos:      0,
+		CurItem:   nil,
+		HeadItem:  nil,
+		RdIfValue: nil,
+		Desc:      "",
+		ErrStr:    "",
+	}
+}
+
+/* 大对象 数据填入
+* Lob_ptr 大对象指针 由XGC_Create_Lob生成,
+* data  数据缓冲区,其内容长度 由 len决定
+* len 数据缓冲区填入数据的长度,  数据存放入大对象 内部是分片存放的,非一块整的大对象,因此较为灵活
+* 注意 当 len 填入长度是 -1 表示整个大对象写入最终完成,此时data区域不起作用,仅标识大对象录入最终完成。
+* 如果最终没有 len 填入长度是 -1 那么无法标识 大对象的录入已经完成,数据录入会失败
+* 返回值 0
+ */
+func (pLob *XuguLob) putLobData(data []byte, len int) (int, error) {
+	if len == -1 {
+		pLob.State = 2
+		return 0, errors.New("LOB data input  len  == -1")
+	}
+	if len == 0 || data == nil {
+		return 0, errors.New("LOB data input  len  == 0 or data == nil")
+	}
+	reLen := len
+	sDataOff := 0
+	if pLob.ChunkNum == 0 {
+
+		pLob.HeadItem = new(LobDataItem)
+		pLob.CurItem = pLob.HeadItem
+		pLob.CurItem.ItemData = make([]byte, LOB_PICE)
+		pLob.CurItem.ChunkNo = 0 // begin from 0
+		pLob.ChunkNum = 1
+
+		cpLen := minInt(reLen, LOB_PICE)
+		//memcpy(pLob->p_cur_item->p_item_data, data, cp_len);
+		copy(pLob.CurItem.ItemData, data)
+		sDataOff += cpLen
+		pLob.DataOff = int64(cpLen)
+		reLen -= cpLen
+	}
+	for reLen != 0 { //=================满了 填入前半段////
+		itemOff := pLob.DataOff % LOB_PICE
+		if itemOff == 0 {
+			pLob.CurItem.Next = new(LobDataItem)
+			pLob.CurItem.Next.ChunkNo = pLob.ChunkNum
+			pLob.ChunkNum++
+			pLob.CurItem = pLob.CurItem.Next
+			//copy data
+			cpLen := minInt(reLen, LOB_PICE)
+			//memcpy(pLob->p_cur_item->p_item_data, data, cp_len);
+			copy(pLob.CurItem.ItemData, data[sDataOff:])
+			sDataOff += cpLen
+			pLob.DataOff += int64(cpLen)
+			reLen -= cpLen
+		} else {
+			cpLen := minInt(reLen, int(LOB_PICE-itemOff))
+			//memcpy(pLob->p_cur_item->p_item_data, data, cp_len);
+			copy(pLob.CurItem.ItemData[cpLen:], data[sDataOff:])
+			sDataOff += cpLen
+			pLob.DataOff += int64(cpLen)
+			reLen -= cpLen
+
+		}
+	}
+	pLob.State = 1
+	return 0, nil
+
+}
+
+func minInt(a, b int) int {
+	if a < b {
+		return a
+	}
+	return b
+}

+ 317 - 0
xugu/xugusql_auxi.go

@@ -0,0 +1,317 @@
+package xugu
+
+import (
+	"C"
+	"database/sql/driver"
+	"errors"
+	"fmt"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// Auxiliary Struct
+type __Value struct {
+	// Boolean value, if the value is true, it means that the data
+	// type of the current field is a large object data type
+	islob bool
+
+	// A pointer of * C.char data type, usually
+	// pointing to the address of the parameter value to be bound
+	value *C.char
+	plob  *XuguLob
+
+	// length usually specifies the true length of the parameter
+	// data to be bound
+	length C.int
+
+	// buff usually specifies the memory buffer
+	// size of the parameter data to be bound
+	buff C.int
+
+	// When parameter binding, specify the data type of the field in the table
+	types int
+
+	// Return code
+	rcode C.int
+}
+
+type parse struct {
+	// bind_type is used to identify the type of parameter binding.
+	// Parameter binding types include binding by parameter name
+	// and binding by parameter placeholder
+	bind_type int
+
+	// param_count is used to specify the number
+	// of parameters that need to be bound in the SQL statement
+	param_count int
+
+	// When the parameter binding type is binding
+	// by parameter name, param_names is a collection of parameter names
+	param_names []*C.char
+
+	Val []__Value
+
+	// When the parameter binding type is binding
+	// by parameter placeholder, position identifies the parameter position
+	position int
+}
+
+type ParseParam interface {
+	// Conversion parameter data type
+	assertParamType(driver.Value, int) error
+	// Number of parsing parameters
+	assertParamCount(string) int
+
+	// Parse parameter binding type (binding type by parameter name
+	// and binding type by parameter position)
+	assertBindType(string) int
+
+	// Parse parameter name
+	assertParamName(string) error
+}
+
+func namedValueToValue(named []driver.NamedValue) ([]driver.Value, error) {
+	__Par := make([]driver.Value, len(named))
+
+	for pos, Param := range named {
+		if len(Param.Name) > 0 {
+			return nil, errors.New("Driver does not support the use of Named Parameters")
+		}
+		__Par[pos] = Param.Value
+	}
+
+	return __Par, nil
+}
+
+func (self *parse) assertParamType(dV driver.Value, pos int) error {
+
+	var dest __Value
+	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 := CreateLob()
+		dest.plob = &re
+
+		srcv, ok := dV.([]byte)
+		if !ok {
+
+			news := errorNews("[]byte")
+			return errors.New(news)
+		}
+		dest.plob.putLobData(srcv, len(srcv))
+
+		dest.plob.putLobData(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")
+	}
+
+	self.position = pos
+	self.Val = append(self.Val, dest)
+
+	return nil
+}
+
+func errorNews(str string) string {
+	return fmt.Sprintf("[%s] asserting data type failed.", str)
+}
+
+func (self *parse) assertParamCount(query string) int {
+
+	if self.bind_type == 0 {
+		self.bind_type = self.assertBindType(query)
+	}
+
+	switch self.bind_type {
+	case BIND_PARAM_BY_POS:
+		self.param_count = strings.Count(query, "?")
+	case BIND_PARAM_BY_NAME:
+
+		self.param_count = 0
+		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
+				}
+
+				if query[tmp] == ',' || query[tmp] == '(' {
+					self.param_count++
+				}
+				break
+			}
+			phead = pos
+		}
+	}
+
+	return self.param_count
+}
+
+func (self *parse) assertBindType(query string) int {
+
+	self.bind_type = strings.IndexByte(query, '?')
+	if self.bind_type != -1 {
+		return BIND_PARAM_BY_POS
+	}
+
+	return BIND_PARAM_BY_NAME
+}
+
+func (self *parse) assertParamName(query string) error {
+
+	if self.param_count <= 0 {
+		self.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] == ' ' {
+						self.param_names = append(self.param_names, C.CString(query[pos+1:parg]))
+						break
+					}
+				}
+			}
+			break
+		}
+
+		phead = pos
+	}
+
+	return nil
+}

+ 187 - 19
xugu/xugusql_connector.go

@@ -3,19 +3,67 @@ package xugu
 import (
 	"context"
 	"database/sql/driver"
-	"errors"
 	"fmt"
 	"net"
+	"strings"
 	"time"
 )
 
+// SQL类型常量
+const (
+	SQL_UNKNOWN = iota
+	SQL_SELECT
+	SQL_INSERT
+	SQL_UPDATE
+	SQL_DELETE
+	SQL_CREATE
+	SQL_ALTER
+	SQL_PROCEDURE
+	SQL_OTHER
+)
+const (
+	XG_OK = iota
+)
+
+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
+)
+
 type connector struct {
 	dsn string
 }
 
-func NewConnector(dsn string) *connector {
-	return &connector{dsn: dsn}
-}
+// func NewConnector(dsn string) *connector {
+// 	dsnL := strings.ToLower(dsn)
+// 	fmt.Println("dsnL: ", dsnL)
+// 	return &connector{dsn: dsnL}
+// }
 
 var IPS_COUNTER int = 0
 
@@ -32,11 +80,16 @@ func (self *connector) Connect(ctx context.Context) (driver.Conn, error) {
 	obj := &xugusqlConn{conn: nil}
 	//connKeyValue := C.CString(self.dsn)
 
+	dsnConfig := parseDSN(self.dsn)
+	fmt.Println("self.dsn:", strings.ToLower(self.dsn))
+	obj.dsnConfig = dsnConfig
+	fmt.Println("connector: ", self.dsn)
+	fmt.Println("dsnConfig: ", dsnConfig)
 	defer func() {
 		//	cgo_c_free(unsafe.Pointer(connKeyValue))
 	}()
-	fmt.Println("Connect")
-	re, _ := xgc_connect(ctx, self.dsn, &obj.conn)
+
+	re, _ := xg_connect(ctx, obj)
 	if re < 0 {
 		return nil, obj.get_error()
 		// pos := strings.Index(strings.ToUpper(self.dsn), "IPS=")
@@ -56,18 +109,22 @@ func (self *connector) Connect(ctx context.Context) (driver.Conn, error) {
 	return obj, nil
 }
 
-func xgc_connect(ctx context.Context, pdsn string, __pConn *net.Conn) (int, error) {
-	fmt.Println("xgc_connect pdsn:", pdsn)
-	return XGSOpenConn(ctx, pdsn, __pConn)
+func xg_connect(ctx context.Context, __pConn *xugusqlConn) (int, error) {
+
+	return xgSockOpenConn(ctx, __pConn)
 }
-func xgc_connect_ips(ctx context.Context, pdsn *string, __pConn *net.Conn) int {
+
+// ips未实现
+func xg_connect_ips(ctx context.Context, __pConn *net.Conn) int {
 	return 0
 }
 
-func XGSOpenConn(ctx context.Context, pdsn string, __pConn *net.Conn) (int, error) {
+func xgSockOpenConn(ctx context.Context, __pConn *xugusqlConn) (int, error) {
 	fmt.Println("XGSOpenConn")
+
 	nd := net.Dialer{Timeout: 5 * time.Second}
-	netConn, err := nd.DialContext(ctx, "tcp", pdsn)
+
+	netConn, err := nd.DialContext(ctx, "tcp", fmt.Sprintf("%s:%s", __pConn.IP, __pConn.Port))
 	if err != nil {
 		fmt.Println("tcp", err)
 		return -8, err
@@ -81,18 +138,19 @@ func XGSOpenConn(ctx context.Context, pdsn string, __pConn *net.Conn) (int, erro
 	}
 
 	//发送
+	//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' "
-
-	_, err = netConn.Write([]byte(message))
+	//	message := "login   database = 'SYSTEM' user = 'SYSDBA'  password = 'SYSDBA' version='201' "
+	dsnMessage := generateLoginString(__pConn.dsnConfig)
+	_, err = netConn.Write([]byte(dsnMessage))
 	if err != nil {
 		fmt.Println("发送数据失败:", err)
 
 	}
-	fmt.Println("数据已发送:", message)
+	fmt.Println("数据已发送:", dsnMessage)
 
 	buffer := make([]byte, 1024)
-	n, err := netConn.Read(buffer)
+	n, err := __pConn.conn.Read(buffer)
 	if err != nil {
 		fmt.Println("Error reading from server:", err)
 
@@ -101,7 +159,117 @@ func XGSOpenConn(ctx context.Context, pdsn string, __pConn *net.Conn) (int, erro
 	return 1, nil
 }
 
-func (self *xugusqlConn) get_error() error {
+func parseDSN(dsn string) dsnConfig {
+	// Initialize a dsnConfig struct
+	var config dsnConfig
+
+	// Split the string by semicolons
+	pairs := strings.Split(dsn, ";")
 
-	return errors.New("errors")
+	// 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
 }

+ 300 - 0
xugu/xugusql_fields.go

@@ -0,0 +1,300 @@
+package xugu
+
+import (
+	"database/sql"
+	"reflect"
+	"time"
+)
+
+type NullTime struct {
+	Time  time.Time
+	Valid bool // Valid is true if Time is not NULL
+}
+
+type xugusqlField 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
+}
+
+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
+)
+
+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
+
+	fieldTypeInterval    fieldType = 21
+	fieldTypeIntervalY2M fieldType = 28
+	fieldTypeIntervalD2S fieldType = 31
+	fieldTypeLob         fieldType = 40
+	fieldTypeClob        fieldType = 41
+	fieldTypeBlob        fieldType = 42
+)
+
+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 (self *xugusqlField) typeDatabaseName() string {
+	switch self.fieldType {
+	case fieldTypeBool:
+		return "BOOLEAN"
+	case fieldTypeChar:
+		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 *xugusqlField) 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,
+		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))
+)

+ 110 - 8
xugu/xugusql_pconn.go

@@ -1,9 +1,13 @@
 package xugu
 
 import (
+	"bytes"
+	"context"
 	"database/sql/driver"
+	"errors"
 	"fmt"
 	"net"
+	"sync"
 )
 
 type xugusqlConn struct {
@@ -11,29 +15,127 @@ type xugusqlConn struct {
 	/* xugusqlResult */
 	affectedRows int
 	insertId     int
+
+	havePrepare     int //default 0
+	prepareNo       int //fashengqi
+	params          *XGCSParam
+	prepareName     string
+	presPrepareCata *Result
+
+	mu sync.Mutex
+
+	useSSL      bool
+	turingDataR interface{} // 加密相关的数据,具体类型根据实际情况定义
+
+	errStr []byte
+
+	dsnConfig
+	Conn_Attrs
+}
+
+type Conn_Attrs struct {
+	bkChar   byte
+	sendBuff bytes.Buffer
 }
 
-func (self *xugusqlConn) Begin() (driver.Tx, error) {
+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 *xugusqlConn) get_error() error {
 
-	err := self.exec("set auto_commit off;")
+	return nil
+}
+func (xgConn *xugusqlConn) Begin() (driver.Tx, error) {
+
+	err := xgConn.exec("set auto_commit off;")
 	if err != nil {
-		return nil, self.get_error()
+		return nil, xgConn.get_error()
 	}
 
-	return &xugusqlTx{tconn: self}, nil
+	return &xugusqlTx{tconn: xgConn}, nil
 }
 
-func (self *xugusqlConn) exec(query string) error {
+func (xgConn *xugusqlConn) exec(query string) error {
 
 	return nil
 }
 
-func (self *xugusqlConn) Prepare(query string) (driver.Stmt, error) {
+func (xgConn *xugusqlConn) Prepare(query string) (driver.Stmt, error) {
+
+	switch xgSqlType(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.")
+	}
 
-	return nil, nil
+	stmt := &xugusqlStmt{
+		stmt_conn:   xgConn.conn,
+		prepared:    false,
+		prename:     "",
+		curopend:    false,
+		curname:     "",
+		param_count: 0,
+		result:      nil,
+		mysql:       query,
+	}
+	var sql string
+	re, err := xgSockPrepare2(xgConn, sql, &xgConn.prepareName)
+	if re < 0 || err != nil {
+		return nil, xgConn.get_error()
+	}
+
+	stmt.prepared = true
+
+	return stmt, nil
 }
 
-func (self *xugusqlConn) Close() error {
+func (xgConn *xugusqlConn) Close() error {
 	fmt.Println("Close connection")
+	err := xgConn.conn.Close()
+	if err != nil {
+		fmt.Println("Close connection error")
+		return err
+	}
+	return nil
+}
+
+func (xgConn *xugusqlConn) Ping(ctx context.Context) error {
+
+	// sql := C.CString("select count(*) from dual;")
+	// defer func() {
+	// 	cgo_c_free(unsafe.Pointer(sql))
+	// }()
+
+	// var fieldCount, effectCount C.int
+	// var rowCount C.longlong
+	// var result unsafe.Pointer
+
+	// re := cgo_xgc_exec_with_reader(&self.conn, sql, &result,
+	// 	&fieldCount, &rowCount, &effectCount)
+	// if re < 0 {
+	// 	return self.get_error()
+	// }
+
 	return nil
 }

+ 91 - 0
xugu/xugusql_result.go

@@ -0,0 +1,91 @@
+package xugu
+
+
+
+type xugusqlResult struct {
+
+	// Returns the number of rows affected
+	// by update, delete and other related operations
+	// 返回受 update、delete 和其他相关操作影响的行数
+	affectedRows int64
+
+	// Returns the GUID number of
+	// the insert operation (not supported)
+	// 返回 insert 操作的 GUID 编号(不支持)
+	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.
+// LastInsertId 返回数据库响应命令生成的整数。
+// 通常这是在插入新行时来自“自动递增”列的值。
+// 并非所有数据库都支持此功能,且此类语句的语法各不相同。
+func (self *xugusqlResult) LastInsertId() (int64, error) {
+	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.
+// RowsAffected 返回受 update、insert 或 delete 影响的行数。
+// 并非每个数据库或数据库驱动都支持此功能。
+func (self *xugusqlResult) RowsAffected() (int64, error) {
+	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 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       *xugusqlConn      // 数据库连接属性
+	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          // 插入类型的标记
+}
+
+type RSMemStruct struct {
+	Head    *Block // 头部块
+	CurBlok *Block // 当前块
+}

+ 180 - 0
xugu/xugusql_sock.go

@@ -0,0 +1,180 @@
+package xugu
+
+import (
+	"errors"
+	"fmt"
+	"strings"
+)
+
+// cgo_xgc_sql_type -> fun_sql_type
+// 判断sql类型
+func xgSqlType(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 xgSockPrepare(conn *xugusqlConn, sql string) (int, error) {
+	ret := 0
+
+	//p_params := &conn.params
+	if conn.havePrepare != 0 {
+		ret = xgCmdUnprepare(conn, conn.prepareName)
+		if ret < 0 {
+			return ret, nil
+		}
+		conn.prepareName = ""
+	}
+
+	conn.prepareName = fmt.Sprintf(conn.prepareName+"STC%d", conn.prepareNo)
+	ret, err := xgCmdPrepare(conn, sql, &conn.prepareName)
+	if err != nil {
+		return ret, err
+	}
+	return ret, err
+}
+
+// SQL语句prepare准备执行
+func xgSockPrepare2(conn *xugusqlConn, cmd_sql string, prepareName *string) (int, error) {
+
+	if "" == *prepareName {
+		return xgSockPrepare(conn, cmd_sql)
+	}
+
+	ret := 0
+	pconn := conn
+	//	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(conn, cmd_sql, &conn.prepareName)
+	if err != nil {
+		return 0, err
+	}
+	pconn.havePrepare = old_p // this keeped by prepare_name
+
+	return ret, nil
+}
+
+func xgCmdUnprepare(pconn *xugusqlConn, prepareName string) int {
+	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)
+	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
+}
+
+func xgCmdPrepare(pconn *xugusqlConn, cmd_sql string, prepareName *string) (int, error) {
+
+	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
+}

+ 194 - 0
xugu/xugusql_sock_recv.go

@@ -0,0 +1,194 @@
+package xugu
+
+import (
+	"bytes"
+	"encoding/binary"
+	"errors"
+)
+
+const RETURN_PREPARE_SELECT = 19665
+
+func rhRecvStr(pConn *xugusqlConn, 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 rhRecvChar(pConn *xugusqlConn, valp *byte) (bool, error) {
+
+	var ch [1]byte
+	err := rhRecv(pConn, ch[:], 1)
+	if err != nil {
+		return false, err
+	}
+	*valp = ch[0]
+	return true, nil
+
+}
+
+// TODO : pConn.bkChar != 0x0  这一部分代码需要修改
+func rhRecv(pConn *xugusqlConn, 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 rhRecvInt32(pconn *xugusqlConn, i *uint32) error {
+	var buff [4]byte
+	err := rhRecv(pconn, buff[:], 4)
+	if err != nil {
+		return err
+	}
+
+	// 将大端字节序转换为主机字节序
+	*i = binary.BigEndian.Uint32(buff[:])
+
+	return nil
+}
+
+func xgCacheRecv(pConn *xugusqlConn, buff []byte, dLen int32) (int, error) {
+	return 0, nil
+}
+
+// 从连接中接收字段信息,并将其存储在 Result 结构体中。
+func recvFiledsInfo(pConn *xugusqlConn, 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 *xugusqlConn, 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 recvParamsInfo2(pConn *xugusqlConn, 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
+}

+ 129 - 0
xugu/xugusql_sock_send.go

@@ -0,0 +1,129 @@
+package xugu
+
+import "encoding/binary"
+
+var BUFF_SIZE = 8 * 1024
+
+// EncryptBuff 假设的加密函数
+func EncryptBuff(pconn *xugusqlConn, data []byte) {
+	// 加密逻辑,根据实际情况实现
+}
+
+func sockSendCommand0(conn *xugusqlConn, sql string) (int, error) {
+	cmdLen := len(sql)
+	//发送问号字符 ?
+	ret, err := RhSend(conn, []byte("?"), cmdLen)
+	if err != nil {
+		return 0, err
+	}
+	// Comand_Len
+	lengthInt32 := uint32(conn.sendBuff.Len())
+	ret, err = rhSendInt32(conn, lengthInt32)
+	if err != nil {
+		return 0, err
+	}
+	//  Comand_str
+	ret, err = RhSend(conn, []byte(sql), cmdLen+1)
+	if err != nil {
+		return 0, err
+	}
+
+	// Param_num
+	ret, err = rhSendInt32(conn, 0)
+	if err != nil {
+		return 0, err
+	}
+	return ret, nil
+}
+
+// RhSend Go 版本的 rh_send 函数
+// TODO :加密连接还没有实现
+func RhSend(pconn *xugusqlConn, buff []byte, length int) (int, error) {
+	pconn.mu.Lock()
+	defer pconn.mu.Unlock()
+	//没有内容则不发送
+	if length == 0 {
+		return 0, nil
+	}
+	//连接里是否有加密
+	if pconn.turingDataR != nil && pconn.useSSL {
+		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(buff)
+			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 >= BUFF_SIZE {
+			//缓冲区空间不足:
+			/*- 如果当前缓冲区中的数据加上新数据的长度超过 `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 *xugusqlConn, i uint32) (int, error) {
+
+	// 创建一个 4 字节的缓冲区,用于存储网络字节序的 int32 值
+	var networkBytes [4]byte
+	// 将 int32 值转换为网络字节序(大端字节序)并写入缓冲区
+	binary.BigEndian.PutUint32(networkBytes[:], i)
+
+	return RhSend(pconn, networkBytes[:], 4)
+}
+
+func RhFlush(pconn *xugusqlConn) (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
+}

+ 321 - 0
xugu/xugusql_stmt.go

@@ -0,0 +1,321 @@
+package xugu
+
+import (
+	"database/sql/driver"
+	"errors"
+	"net"
+	"unsafe"
+)
+
+type xugusqlStmt struct {
+
+	// Context connection handle pointer
+	stmt_conn net.Conn
+
+	// Boolean value, used to identify whether
+	// the executed SQL statement has been prepared
+	// 布尔值,用于标识执行的 SQL 语句是否已准备好
+	prepared bool
+	// Accept the prepared code for
+	// the prepared SQL statement
+	// 接受已准备好 SQL 语句的准备代码
+	prename string
+	// Boolean value used to identify
+	// whether the cursor is enabled
+	// 布尔值,用于标识是否启用了游标
+	curopend bool
+	// Cursor name
+	// 游标名称
+	curname string
+	//  The number of parameters
+	// in the executed SQL statement
+	// 执行的 SQL 语句中的参数数量
+	param_count int
+	mysql       string
+	// Context result set handle pointer
+	// 上下文结果集句柄指针
+	result unsafe.Pointer
+}
+
+/* Collect error information from the database server */
+func (self *xugusqlStmt) 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("error")
+}
+
+/* {{ */
+func (self *xugusqlStmt) Close() error {
+
+	if self.curopend {
+		re := cgo_xgc_close_cursor(&self.stmt_conn, self.curname)
+		if re < 0 {
+			return self.get_error()
+		}
+
+		cgo_c_free(unsafe.Pointer(self.curname))
+		self.curname = nil
+		self.curopend = false
+	}
+
+	if self.prepared {
+		re := cgo_xgc_unprepare(&self.stmt_conn, self.prename)
+		if re < 0 {
+			return self.get_error()
+		}
+
+		cgo_c_free(unsafe.Pointer(self.prename))
+		self.prename = nil
+		self.prepared = false
+	}
+
+	return nil
+}
+
+/* {{ */
+func (self *xugusqlStmt) NumInput() int {
+
+	parser := &parse{
+		bind_type:   0,
+		param_count: 0,
+		position:    0,
+	}
+
+	return parser.assertParamCount(self.mysql)
+}
+
+// Exec executes a prepared statement with the given arguments and
+// returns a Result summarizing the effect of the statement.
+func (self *xugusqlStmt) Exec(args []driver.Value) (driver.Result, error) {
+
+	switch xgSqlType(self.mysql) {
+	case SQL_SELECT:
+		return nil, errors.New("Exec does not support queries")
+	}
+	if !self.prepared {
+		return nil, errors.New("SQL statement is not Prepared")
+	}
+
+	parser := &parse{
+		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(self.mysql) {
+			return nil, errors.New("The number of parameters does not match")
+		}
+
+		switch parser.assertBindType(self.mysql) {
+		case BIND_PARAM_BY_POS:
+			for pos, param := range parser.Val {
+				if !param.islob {
+					re := cgo_xgc_bindparambypos(&self.stmt_conn, pos+1,
+						SQL_PARAM_INPUT, param.types,
+						unsafe.Pointer(param.value), param.buff, &param.length)
+					if re < 0 {
+						return nil, self.get_error()
+					}
+				} else {
+					re := cgo_xgc_bindparambypos(&self.stmt_conn, pos+1,
+						SQL_PARAM_INPUT, param.types,
+						unsafe.Pointer(&param.plob), param.buff, &param.length)
+					if re < 0 {
+						return nil, self.get_error()
+					}
+				}
+			}
+
+		case BIND_PARAM_BY_NAME:
+			parser.assertParamName(self.mysql)
+			for pos, param := range parser.Val {
+				if !param.islob {
+					re := cgo_xgc_bindparambyname(&self.stmt_conn, parser.param_names[pos],
+						SQL_PARAM_INPUT, param.types, unsafe.Pointer(param.value),
+						param.buff, &param.rcode, &param.length)
+					if re < 0 {
+						return nil, self.get_error()
+					}
+				} else {
+					re := cgo_xgc_bindparambyname(&self.stmt_conn, parser.param_names[pos],
+						SQL_PARAM_INPUT, param.types, unsafe.Pointer(&param.plob),
+						param.buff, &param.rcode, &param.length)
+					if re < 0 {
+						return nil, self.get_error()
+					}
+				}
+			}
+		}
+	}
+
+	defer func() {
+		cgo_c_free(unsafe.Pointer(sql))
+		for pos, param := range parser.Val {
+			if parser.bind_type == BIND_PARAM_BY_NAME {
+				cgo_c_free(unsafe.Pointer(parser.param_names[pos]))
+			}
+
+			if !param.islob {
+				cgo_c_free(unsafe.Pointer(param.value))
+			} else {
+				cgo_xgc_lob_distroy(&param.plob)
+			}
+		}
+
+	}()
+
+	result := &xugusqlResult{
+		affectedRows: 0,
+		insertId:     0,
+	}
+
+	re := cgo_xgc_execute(&self.stmt_conn, self.prename, self.curname, &self.result)
+	if re < 0 {
+		return nil, self.get_error()
+	}
+
+	var pCT, pCC, pRC, pEC C.int
+	var pID = cgo_c_calloc(ROWID_BUFF_SIZE)
+
+	re = cgo_xgc_get_result_set(&self.result, &pCT, &pCC, &pRC, &pEC, pID)
+	if re < 0 {
+		return nil, self.get_error()
+	}
+
+	cgo_c_free(unsafe.Pointer(pID))
+	result.affectedRows = int64(pEC)
+
+	return result, nil
+}
+
+// QueryContext executes a prepared query statement with the given arguments
+// and returns the query results as a *Rows.
+func (self *xugusqlStmt) Query(args []driver.Value) (driver.Rows, error) {
+
+	sql := C.CString(self.mysql)
+	if cgo_xgc_sql_type(sql) != SQL_SELECT {
+		return nil, errors.New("The executed SQL statement is not a SELECT")
+	}
+
+	if !self.prepared {
+		return nil, errors.New("SQL statement is not Prepared")
+	}
+
+	parser := &parse{
+		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(self.mysql) {
+			return nil, errors.New("The number of parameters does not match")
+		}
+
+		switch parser.assertBindType(self.mysql) {
+		case BIND_PARAM_BY_POS:
+			for pos, param := range parser.Val {
+				if !param.islob {
+					re := cgo_xgc_bindparambypos(&self.stmt_conn, pos+1,
+						SQL_PARAM_INPUT, param.types,
+						unsafe.Pointer(param.value), param.buff, &param.length)
+					if re < 0 {
+						return nil, self.get_error()
+					}
+				} else {
+
+					re := cgo_xgc_bindparambypos(&self.stmt_conn, pos+1,
+						SQL_PARAM_INPUT, param.types,
+						unsafe.Pointer(&param.plob), param.buff, &param.length)
+					if re < 0 {
+						return nil, self.get_error()
+					}
+				}
+			}
+
+		case BIND_PARAM_BY_NAME:
+			parser.assertParamName(self.mysql)
+			for pos, param := range parser.Val {
+				if !param.islob {
+					re := cgo_xgc_bindparambyname(&self.stmt_conn,
+						parser.param_names[pos],
+						SQL_PARAM_INPUT, param.types, unsafe.Pointer(param.value),
+						param.buff, &param.rcode, &param.length)
+					if re < 0 {
+						return nil, self.get_error()
+					}
+				} else {
+
+					re := cgo_xgc_bindparambyname(&self.stmt_conn,
+						parser.param_names[pos],
+						SQL_PARAM_INPUT, param.types, unsafe.Pointer(&param.plob),
+						param.buff, &param.rcode, &param.length)
+					if re < 0 {
+						return nil, self.get_error()
+					}
+				}
+			}
+		}
+	}
+
+	defer func() {
+		cgo_c_free(unsafe.Pointer(sql))
+		for pos, param := range parser.Val {
+			if parser.bind_type == BIND_PARAM_BY_NAME {
+				cgo_c_free(unsafe.Pointer(parser.param_names[pos]))
+			}
+
+			if !param.islob {
+				cgo_c_free(unsafe.Pointer(param.value))
+			} else {
+				cgo_xgc_lob_distroy(&param.plob)
+			}
+		}
+
+	}()
+
+	//if self.curname == nil {
+	//	self.curname = cgo_c_calloc(CURSOR_NAME_BUFF_SIZE)
+	//}
+
+	re := cgo_xgc_execute(&self.stmt_conn, self.prename, self.curname, &self.result)
+	if re < 0 {
+		return nil, self.get_error()
+	}
+
+	//re = cgo_xgc_fetch_with_cursor(&self.stmt_conn, self.curname, &self.result)
+	//if re < 0 {
+	//	return nil, self.get_error()
+	//}
+
+	//self.curopend = true
+	return &xugusqlRows{
+		result:    self.result,
+		prepared:  self.prepared,
+		rows_conn: self.stmt_conn,
+	}, nil
+
+}