|
@@ -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, ¶m.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(¶m.plob), param.buff, ¶m.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, ¶m.rcode, ¶m.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(¶m.plob),
|
|
|
+ param.buff, ¶m.rcode, ¶m.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(¶m.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, ¶m.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(¶m.plob), param.buff, ¶m.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, ¶m.rcode, ¶m.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(¶m.plob),
|
|
|
+ param.buff, ¶m.rcode, ¶m.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(¶m.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
|
|
|
+
|
|
|
+}
|