예제 #1
0
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
}
예제 #2
0
//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
}