func (conn *Conn) fetchResults() ([]*Result, error) { results := make([]*Result, 0) for { erc := C.dbresults(conn.dbproc) if erc == C.NO_MORE_RESULTS { break } if erc == C.FAIL { return nil, conn.raise(errors.New("dbresults failed")) } result := NewResult() conn.currentResult = result cols := int(C.dbnumcols(conn.dbproc)) columns := make([]column, cols) for i := 0; i < cols; i++ { no := C.int(i + 1) name := C.GoString(C.dbcolname(conn.dbproc, no)) size := C.dbcollen(conn.dbproc, no) typ := C.dbcoltype(conn.dbproc, no) if typ == SYBUNIQUE { size = 36 } bindTyp, typ := dbbindtype(typ) result.addColumn(name, int(size), int(typ)) if bindTyp == C.NTBSTRINGBIND && C.SYBCHAR != typ && C.SYBTEXT != typ { size = C.DBINT(C.dbwillconvert(typ, C.SYBCHAR)) } col := &columns[i] col.name = name col.typ = int(typ) col.size = int(size) col.bindTyp = int(bindTyp) col.buffer = make([]byte, size+1) erc = C.dbbind(conn.dbproc, no, bindTyp, size+1, (*C.BYTE)(&col.buffer[0])) //fmt.Printf("dbbind %d, %d, %v\n", bindTyp, size+1, col.buffer) if erc == C.FAIL { return nil, errors.New("dbbind failed: no such column or no such conversion possible, or target buffer too small") } erc = C.dbnullbind(conn.dbproc, no, &col.status) if erc == C.FAIL { return nil, errors.New("dbnullbind failed") } } for i := 0; ; i++ { rowCode := C.dbnextrow(conn.dbproc) if rowCode == C.NO_MORE_ROWS { break } if rowCode == C.REG_ROW { for j := 0; j < cols; j++ { col := columns[j] //fmt.Printf("col: %#v\nvalue:%s\n", col, col.Value()) result.addValue(i, j, col.Value()) } } } result.RowsAffected = int(C.my_dbcount(conn.dbproc)) if C.dbhasretstat(conn.dbproc) == C.TRUE { result.ReturnValue = int(C.dbretstatus(conn.dbproc)) } results = append(results, result) conn.currentResult = nil } if len(conn.Error) > 0 { return results, conn.raise(nil) } return results, nil }
//Execute stored procedure by name and list of params. // //Example: // conn.ExecSp("sp_help", "authors") func (conn *Conn) ExecSp(spName string, params ...interface{}) (*SpResult, error) { //hold references to data sent to the C code until the end of this function //without this GC could remove something used later in C, and we will get SIGSEG refHolder := make([]*[]byte, 0) conn.clearMessages() name := C.CString(spName) defer C.free(unsafe.Pointer(name)) if C.dbrpcinit(conn.dbproc, name, 0) == C.FAIL { return nil, conn.raiseError("dbrpcinit failed") } //input params spParams, err := conn.getSpParams(spName) if err != nil { return nil, err } for i, spParam := range spParams { //get datavalue for the suplied stored procedure parametar var datavalue *C.BYTE datalen := C.DBINT(0) if i < len(params) { param := params[i] if param != nil { data, err := typeToSqlBuf(int(spParam.UserTypeId), param) if err != nil { return nil, err } if len(data) > 0 { datalen = C.DBINT(len(data)) datavalue = (*C.BYTE)(unsafe.Pointer(&data[0])) refHolder = append(refHolder, &data) } } } //set parametar valus, call dbrpcparam if i < len(params) || spParam.IsOutput { maxOutputSize := C.DBINT(0) status := C.BYTE(0) if spParam.IsOutput { status = C.DBRPCRETURN maxOutputSize = C.DBINT(spParam.MaxLength) } paramname := C.CString(spParam.Name) defer C.free(unsafe.Pointer(paramname)) if C.dbrpcparam(conn.dbproc, paramname, status, C.int(spParam.UserTypeId), maxOutputSize, datalen, datavalue) == C.FAIL { return nil, errors.New("dbrpcparam failed") } } } //execute if C.dbrpcsend(conn.dbproc) == C.FAIL { return nil, conn.raiseError("dbrpcsend failed") } //results result := NewSpResult() result.results, err = conn.fetchResults() if err != nil { return nil, conn.raise(err) } //return status if C.dbhasretstat(conn.dbproc) == C.TRUE { result.status = int(C.dbretstatus(conn.dbproc)) } //read output params numOutParams := int(C.dbnumrets(conn.dbproc)) result.outputParams = make([]*SpOutputParam, numOutParams) for i := 1; i <= numOutParams; i++ { j := C.int(i) len := C.dbretlen(conn.dbproc, j) name := C.GoString(C.dbretname(conn.dbproc, j)) typ := int(C.dbrettype(conn.dbproc, j)) data := C.GoBytes(unsafe.Pointer(C.dbretdata(conn.dbproc, j)), len) value := sqlBufToType(typ, data) param := &SpOutputParam{Name: name, Value: value} result.outputParams[i-1] = param } return result, nil }