Skip to content

Commit

Permalink
Converting go to sql datatypes.
Browse files Browse the repository at this point in the history
  • Loading branch information
ianic committed Jan 5, 2014
1 parent 6f6ad64 commit f8253f0
Show file tree
Hide file tree
Showing 9 changed files with 606 additions and 126 deletions.
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -22,3 +22,4 @@ _testmain.go
*.exe
/example/example
/gofreetds.test
/example/sqlx
98 changes: 86 additions & 12 deletions conn.go
Expand Up @@ -5,10 +5,12 @@ import (
"errors"
"unsafe"
"strings"
// "log"
)
// "log"
"encoding/binary"
"bytes"
)

/*
/*
#cgo LDFLAGS: -lsybdb
#include <stdio.h>
#include <stdlib.h>
Expand Down Expand Up @@ -64,7 +66,7 @@ type Conn struct {
Message string
currentResult *Result
credentials
}
}

func (conn *Conn) addMessage(msg string) {
if len(conn.Message) > 0 {
Expand All @@ -74,7 +76,7 @@ func (conn *Conn) addMessage(msg string) {
if conn.currentResult != nil {
conn.currentResult.Message += msg
}
}
}

func (conn *Conn) addError(err string) {
if len(conn.Error) > 0 {
Expand Down Expand Up @@ -105,7 +107,7 @@ func connectWithCredentials(crd *credentials) (*Conn, error) {
}

func (conn *Conn) connect() (*Conn, error){
// log.Printf("freetds connecting to %s@%s.%s", conn.user, conn.host, conn.database)
// log.Printf("freetds connecting to %s@%s.%s", conn.user, conn.host, conn.database)
conn.close()
conn.clearMessages()
dbproc, err := conn.getDbProc()
Expand All @@ -120,7 +122,7 @@ func (conn *Conn) connect() (*Conn, error){
conn.close()
return nil, err
}
// log.Printf("freetds connected to %s@%s.%s", conn.user, conn.host, conn.database)
// log.Printf("freetds connected to %s@%s.%s", conn.user, conn.host, conn.database)
return conn, nil
}

Expand Down Expand Up @@ -235,11 +237,12 @@ func (conn *Conn) exec(sql string) ([]*Result, error) {
return nil, errors.New("dbsqlexec failed")
}
}
rst, err := conn.fetchResults()
if err == nil && len(conn.Error) > 0 {
return rst, errors.New(conn.Error)
}
return rst, err
return conn.fetchResults()
// rst, err := conn.fetchResults()
// if err == nil && len(conn.Error) > 0 {
// return rst, errors.New(conn.Error)
// }
// return rst, err
}

func (conn *Conn) isDead() bool {
Expand Down Expand Up @@ -272,3 +275,74 @@ func (conn *Conn) SelectValue(sql string) (interface{}, error){
}
return results[0].Rows[0][0], nil
}

func (conn *Conn) execSp(spName string, params ...interface{}) ([]*Result, int, error) {
conn.clearMessages()
if C.dbrpcinit(conn.dbproc, C.CString(spName), 0) == C.FAIL {
return nil, -1, errors.New("dbrpcinit failed")
}
if len(params) == 1 {
//conn.getSpParams(spName)

datalen, datavalue, err := toRpcParam(C.SYBINT4, params[0])
if err != nil {
return nil, -1, err
}
if C.dbrpcparam(conn.dbproc, C.CString("@p1"), C.BYTE(0), C.SYBINT4, 0, datalen, datavalue) == C.FAIL {
return nil, -1, errors.New("dbrpcparam failed")
}
}
if C.dbrpcsend(conn.dbproc) == C.FAIL {
return nil, -1, errors.New("dbrpcsend failed")
}
results, err := conn.fetchResults()
status := -1
if C.dbhasretstat(conn.dbproc) == C.TRUE {
status = int(C.dbretstatus(conn.dbproc))
}
return results, status, err
}


func toRpcParam(datatype C.int, value interface{}) (datalen C.DBINT, datavalue *C.BYTE, err error) {
buf := new(bytes.Buffer)
switch datatype {
case C.SYBINT4: {
var int32Value int32
switch value.(type) {
case int: {
intValue, _ := value.(int)
int32Value = int32(intValue)
}
case int32:
int32Value, _ = value.(int32)
case int64:
intValue, _ := value.(int64)
int32Value = int32(intValue)
default: {
err = errors.New("failed to convert to int32")
return
}
}
err = binary.Write(buf, binary.LittleEndian, int32Value)
}
}
byt := buf.Bytes()
datavalue = (*C.BYTE)(unsafe.Pointer(&byt[0]))
datalen = C.DBINT(len(byt))
return
}


func (conn *Conn) getSpParams(spName string) (*Result, error) {
sql := fmt.Sprintf(`select name, parameter_id, user_type_id, is_output, max_length, precision, scale
from sys.parameters
where object_id = object_id('%s')
order by parameter_id`, spName)
results, err := conn.exec(sql)
if err != nil {
return nil, err
}
PrintResults(results)
return results[0], nil
}
70 changes: 70 additions & 0 deletions conn_sp_test.go
@@ -0,0 +1,70 @@
package freetds

import (
"github.com/stretchrcom/testify/assert"
"testing"
"strings"
)

func TestExecSp(t *testing.T) {
conn := ConnectToTestDb(t)
results, _, err := conn.execSp("sp_who")
assert.Nil(t, err)
assert.NotNil(t, results)
assert.Equal(t, 1, len(results))


}

func TestExecSpReturnValue(t *testing.T) {
conn := ConnectToTestDb(t)
err := createProcedure(conn, "test_return_value", " as return 123")
assert.Nil(t, err)
results, status, err := conn.execSp("test_return_value")
assert.Nil(t, err)
assert.Equal(t, 0, len(results))
assert.Equal(t, 123, status)
}


func TestExecSpResults(t *testing.T) {
conn := ConnectToTestDb(t)
err := createProcedure(conn, "test_results", " as select 1 one; select 2 two; return 456")
assert.Nil(t, err)
results, status, err := conn.execSp("test_results")
assert.Nil(t, err)
assert.Equal(t, 2, len(results))
assert.Equal(t, 456, status)
}

func TestExecSpInputParams(t *testing.T) {
conn := ConnectToTestDb(t)
err := createProcedure(conn, "test_input_params", "@p1 int = 0 as select @p1 = @p1 + 123; return @p1")
assert.Nil(t, err)
results, status, err := conn.execSp("test_input_params", 123)
assert.Nil(t, err)
assert.Equal(t, 0, len(results))
assert.Equal(t, 246, status)
}



func createProcedure(conn *Conn, name, body string) error {
drop := `
if exists(select * from sys.procedures where name = 'sp_name')
drop procedure sp_name
`
create := `
create procedure sp_name
sp_body
`
drop = strings.Replace(drop, "sp_name", name, -1)
create = strings.Replace(create, "sp_name", name, -1)
create = strings.Replace(create, "sp_body", body, -1)
_, err := conn.Exec(drop)
if err != nil {
return err
}
_, err = conn.Exec(create)
return err
}
1 change: 1 addition & 0 deletions conn_test.go
Expand Up @@ -286,3 +286,4 @@ func BenchmarkParalelConnectExecute(b *testing.B) {
}
}
}

0 comments on commit f8253f0

Please sign in to comment.