run gofmt

This commit is contained in:
Cadey Ratio 2017-07-15 13:50:10 -07:00
parent edf576cd33
commit 40a5e952d2
10 changed files with 367 additions and 372 deletions

125
api.go
View File

@ -33,69 +33,69 @@ import "time"
func (conn *Connection) rqliteApiGet(apiOp apiOperation) ([]byte, error) { func (conn *Connection) rqliteApiGet(apiOp apiOperation) ([]byte, error) {
var responseBody []byte var responseBody []byte
trace("%s: rqliteApiGet() called",conn.ID) trace("%s: rqliteApiGet() called", conn.ID)
// only api_STATUS now - maybe someday BACKUP // only api_STATUS now - maybe someday BACKUP
if ( apiOp != api_STATUS ) { if apiOp != api_STATUS {
return responseBody, errors.New("rqliteApiGet() called for invalid api operation") return responseBody, errors.New("rqliteApiGet() called for invalid api operation")
} }
// just to be safe, check this // just to be safe, check this
peersToTry := conn.cluster.makePeerList() peersToTry := conn.cluster.makePeerList()
if ( len(peersToTry) < 1 ) { if len(peersToTry) < 1 {
return responseBody, errors.New("I don't have any cluster info") return responseBody, errors.New("I don't have any cluster info")
} }
trace("%s: I have a peer list %d peers long",conn.ID,len(peersToTry)) trace("%s: I have a peer list %d peers long", conn.ID, len(peersToTry))
// failure log is used so that if all peers fail, we can say something // failure log is used so that if all peers fail, we can say something
// about why each failed // about why each failed
failureLog := make([]string,0) failureLog := make([]string, 0)
PeerLoop: PeerLoop:
for peerNum, peerToTry := range peersToTry { for peerNum, peerToTry := range peersToTry {
trace("%s: attemping to contact peer %d",conn.ID,peerNum) trace("%s: attemping to contact peer %d", conn.ID, peerNum)
// docs say default GET policy is up to 10 follows automatically // docs say default GET policy is up to 10 follows automatically
url := conn.assembleURL(api_STATUS,peerToTry) url := conn.assembleURL(api_STATUS, peerToTry)
req, err := http.NewRequest("GET",url,nil) req, err := http.NewRequest("GET", url, nil)
if ( err != nil ) { if err != nil {
trace("%s: got error '%s' doing http.NewRequest", conn.ID,err.Error()) trace("%s: got error '%s' doing http.NewRequest", conn.ID, err.Error())
failureLog = append(failureLog,fmt.Sprintf("%s failed due to %s",url,err.Error())) failureLog = append(failureLog, fmt.Sprintf("%s failed due to %s", url, err.Error()))
continue PeerLoop continue PeerLoop
} }
trace("%s: http.NewRequest() OK") trace("%s: http.NewRequest() OK")
req.Header.Set("Content-Type","application/json") req.Header.Set("Content-Type", "application/json")
client := &http.Client{} client := &http.Client{}
client.Timeout = time.Duration(conn.timeout) * time.Second client.Timeout = time.Duration(conn.timeout) * time.Second
response, err := client.Do(req) response, err := client.Do(req)
if ( err != nil ) { if err != nil {
trace("%s: got error '%s' doing client.Do", conn.ID,err.Error()) trace("%s: got error '%s' doing client.Do", conn.ID, err.Error())
failureLog = append(failureLog,fmt.Sprintf("%s failed due to %s",url,err.Error())) failureLog = append(failureLog, fmt.Sprintf("%s failed due to %s", url, err.Error()))
continue PeerLoop continue PeerLoop
} }
defer response.Body.Close() defer response.Body.Close()
trace("%s: client.Do() OK") trace("%s: client.Do() OK")
responseBody, err := ioutil.ReadAll(response.Body) responseBody, err := ioutil.ReadAll(response.Body)
if ( err != nil ) { if err != nil {
trace("%s: got error '%s' doing ioutil.ReadAll", conn.ID,err.Error()) trace("%s: got error '%s' doing ioutil.ReadAll", conn.ID, err.Error())
failureLog = append(failureLog,fmt.Sprintf("%s failed due to %s",url,err.Error())) failureLog = append(failureLog, fmt.Sprintf("%s failed due to %s", url, err.Error()))
continue PeerLoop continue PeerLoop
} }
trace("%s: ioutil.ReadAll() OK") trace("%s: ioutil.ReadAll() OK")
if ( response.Status != "200 OK" ) { if response.Status != "200 OK" {
trace("%s: got code %s",conn.ID,response.Status) trace("%s: got code %s", conn.ID, response.Status)
failureLog = append(failureLog,fmt.Sprintf("%s failed, got: %s",url,response.Status)) failureLog = append(failureLog, fmt.Sprintf("%s failed, got: %s", url, response.Status))
continue PeerLoop continue PeerLoop
} }
// if we got here, we succeeded // if we got here, we succeeded
trace("%s: api call OK, returning",conn.ID) trace("%s: api call OK, returning", conn.ID)
return responseBody, nil return responseBody, nil
} }
// if we got here, all peers failed. Let's build a verbose error message // if we got here, all peers failed. Let's build a verbose error message
var stringBuffer bytes.Buffer var stringBuffer bytes.Buffer
stringBuffer.WriteString("tried all peers unsuccessfully. here are the results:\n") stringBuffer.WriteString("tried all peers unsuccessfully. here are the results:\n")
for n, v := range failureLog { for n, v := range failureLog {
stringBuffer.WriteString(fmt.Sprintf(" peer #%d: %s\n",n,v)) stringBuffer.WriteString(fmt.Sprintf(" peer #%d: %s\n", n, v))
} }
return responseBody, errors.New(stringBuffer.String()) return responseBody, errors.New(stringBuffer.String())
} }
@ -114,78 +114,80 @@ PeerLoop:
* *****************************************************************/ * *****************************************************************/
func (conn *Connection) rqliteApiPost (apiOp apiOperation, sqlStatements []string) ([]byte, error) { func (conn *Connection) rqliteApiPost(apiOp apiOperation, sqlStatements []string) ([]byte, error) {
var responseBody []byte var responseBody []byte
switch (apiOp) { switch apiOp {
case api_QUERY: case api_QUERY:
trace("%s: rqliteApiGet() post called for a QUERY of %d statements",conn.ID,len(sqlStatements)) trace("%s: rqliteApiGet() post called for a QUERY of %d statements", conn.ID, len(sqlStatements))
case api_WRITE: case api_WRITE:
trace("%s: rqliteApiGet() post called for a QUERY of %d statements",conn.ID,len(sqlStatements)) trace("%s: rqliteApiGet() post called for a QUERY of %d statements", conn.ID, len(sqlStatements))
default: default:
return responseBody, errors.New("weird! called for an invalid apiOperation in rqliteApiPost()") return responseBody, errors.New("weird! called for an invalid apiOperation in rqliteApiPost()")
} }
// jsonify the statements. not really needed in the // jsonify the statements. not really needed in the
// case of api_STATUS but doesn't hurt // case of api_STATUS but doesn't hurt
jStatements , err := json.Marshal(sqlStatements) jStatements, err := json.Marshal(sqlStatements)
if ( err != nil ) { return nil, err } if err != nil {
return nil, err
}
// just to be safe, check this // just to be safe, check this
peersToTry := conn.cluster.makePeerList() peersToTry := conn.cluster.makePeerList()
if ( len(peersToTry) < 1 ) { if len(peersToTry) < 1 {
return responseBody, errors.New("I don't have any cluster info") return responseBody, errors.New("I don't have any cluster info")
} }
// failure log is used so that if all peers fail, we can say something // failure log is used so that if all peers fail, we can say something
// about why each failed // about why each failed
failureLog := make([]string,0) failureLog := make([]string, 0)
PeerLoop: PeerLoop:
for peerNum, peer := range peersToTry { for peerNum, peer := range peersToTry {
trace("%s: trying peer #%d",conn.ID,peerNum) trace("%s: trying peer #%d", conn.ID, peerNum)
// we're doing a post, and the RFCs say that if you get a 301, it's not // we're doing a post, and the RFCs say that if you get a 301, it's not
// automatically followed, so we have to do that ourselves // automatically followed, so we have to do that ourselves
responseStatus := "Haven't Tried Yet" responseStatus := "Haven't Tried Yet"
var url string var url string
for ( responseStatus == "Haven't Tried Yet" || responseStatus == "301 Moved Permanently" ) { for responseStatus == "Haven't Tried Yet" || responseStatus == "301 Moved Permanently" {
url = conn.assembleURL(apiOp,peer) url = conn.assembleURL(apiOp, peer)
req, err := http.NewRequest("POST",url,bytes.NewBuffer(jStatements)) req, err := http.NewRequest("POST", url, bytes.NewBuffer(jStatements))
if ( err != nil ) { if err != nil {
trace("%s: got error '%s' doing http.NewRequest", conn.ID,err.Error()) trace("%s: got error '%s' doing http.NewRequest", conn.ID, err.Error())
failureLog = append(failureLog,fmt.Sprintf("%s failed due to %s",url,err.Error())) failureLog = append(failureLog, fmt.Sprintf("%s failed due to %s", url, err.Error()))
continue PeerLoop continue PeerLoop
} }
req.Header.Set("Content-Type","application/json") req.Header.Set("Content-Type", "application/json")
client := &http.Client{} client := &http.Client{}
response, err := client.Do(req) response, err := client.Do(req)
if ( err != nil ) { if err != nil {
trace("%s: got error '%s' doing client.Do", conn.ID,err.Error()) trace("%s: got error '%s' doing client.Do", conn.ID, err.Error())
failureLog = append(failureLog,fmt.Sprintf("%s failed due to %s",url,err.Error())) failureLog = append(failureLog, fmt.Sprintf("%s failed due to %s", url, err.Error()))
continue PeerLoop continue PeerLoop
} }
defer response.Body.Close() defer response.Body.Close()
responseBody, err = ioutil.ReadAll(response.Body) responseBody, err = ioutil.ReadAll(response.Body)
if ( err != nil ) { if err != nil {
trace("%s: got error '%s' doing ioutil.ReadAll", conn.ID,err.Error()) trace("%s: got error '%s' doing ioutil.ReadAll", conn.ID, err.Error())
failureLog = append(failureLog,fmt.Sprintf("%s failed due to %s",url,err.Error())) failureLog = append(failureLog, fmt.Sprintf("%s failed due to %s", url, err.Error()))
continue PeerLoop continue PeerLoop
} }
responseStatus = response.Status responseStatus = response.Status
if ( responseStatus == "301 Moved Permanently" ) { if responseStatus == "301 Moved Permanently" {
v := response.Header["Location"] v := response.Header["Location"]
failureLog = append(failureLog,fmt.Sprintf("%s redirected me to %s",url,v[0])) failureLog = append(failureLog, fmt.Sprintf("%s redirected me to %s", url, v[0]))
url = v[0] url = v[0]
continue PeerLoop continue PeerLoop
} else if ( responseStatus == "200 OK" ) { } else if responseStatus == "200 OK" {
trace("%s: api call OK, returning",conn.ID) trace("%s: api call OK, returning", conn.ID)
return responseBody, nil return responseBody, nil
} else { } else {
trace("%s: got error in responseStatus: %s", conn.ID, responseStatus) trace("%s: got error in responseStatus: %s", conn.ID, responseStatus)
failureLog = append(failureLog,fmt.Sprintf("%s failed, got: %s",url,response.Status)) failureLog = append(failureLog, fmt.Sprintf("%s failed, got: %s", url, response.Status))
continue PeerLoop continue PeerLoop
} }
} }
@ -195,8 +197,7 @@ PeerLoop:
var stringBuffer bytes.Buffer var stringBuffer bytes.Buffer
stringBuffer.WriteString("tried all peers unsuccessfully. here are the results:\n") stringBuffer.WriteString("tried all peers unsuccessfully. here are the results:\n")
for n, v := range failureLog { for n, v := range failureLog {
stringBuffer.WriteString(fmt.Sprintf(" peer #%d: %s\n",n,v)) stringBuffer.WriteString(fmt.Sprintf(" peer #%d: %s\n", n, v))
} }
return responseBody, errors.New(stringBuffer.String()) return responseBody, errors.New(stringBuffer.String())
} }

View File

@ -37,14 +37,13 @@ import "strings"
* *****************************************************************/ * *****************************************************************/
type peer struct { type peer struct {
hostname string // hostname or "localhost" hostname string // hostname or "localhost"
port string // "4001" or port, only ever used as a string port string // "4001" or port, only ever used as a string
} }
func (p *peer) String() string { func (p *peer) String() string {
return fmt.Sprintf("%s:%s",p.hostname,p.port) return fmt.Sprintf("%s:%s", p.hostname, p.port)
} }
/* ***************************************************************** /* *****************************************************************
@ -56,9 +55,9 @@ func (p *peer) String() string {
* *****************************************************************/ * *****************************************************************/
type rqliteCluster struct { type rqliteCluster struct {
leader peer leader peer
otherPeers []peer otherPeers []peer
conn *Connection conn *Connection
} }
/* ***************************************************************** /* *****************************************************************
@ -72,16 +71,16 @@ type rqliteCluster struct {
* *****************************************************************/ * *****************************************************************/
func (rc *rqliteCluster) makePeerList() []peer { func (rc *rqliteCluster) makePeerList() []peer {
trace("%s: makePeerList() called",rc.conn.ID) trace("%s: makePeerList() called", rc.conn.ID)
var peerList []peer var peerList []peer
peerList = append(peerList,rc.leader) peerList = append(peerList, rc.leader)
for _, p := range rc.otherPeers { for _, p := range rc.otherPeers {
peerList = append(peerList,p) peerList = append(peerList, p)
} }
trace("%s: makePeerList() returning this list:",rc.conn.ID) trace("%s: makePeerList() returning this list:", rc.conn.ID)
for n, v := range peerList { for n, v := range peerList {
trace("%s: makePeerList() peer %d -> %s",rc.conn.ID,n,v.hostname + ":" + v.port) trace("%s: makePeerList() peer %d -> %s", rc.conn.ID, n, v.hostname+":"+v.port)
} }
return peerList return peerList
@ -105,13 +104,13 @@ func (rc *rqliteCluster) makePeerList() []peer {
func (conn *Connection) assembleURL(apiOp apiOperation, p peer) string { func (conn *Connection) assembleURL(apiOp apiOperation, p peer) string {
var stringBuffer bytes.Buffer var stringBuffer bytes.Buffer
if ( conn.wantsHTTPS == true ) { if conn.wantsHTTPS == true {
stringBuffer.WriteString("https") stringBuffer.WriteString("https")
} else { } else {
stringBuffer.WriteString("http") stringBuffer.WriteString("http")
} }
stringBuffer.WriteString("://") stringBuffer.WriteString("://")
if ( conn.username != "" && conn.password != "" ) { if conn.username != "" && conn.password != "" {
stringBuffer.WriteString(conn.username) stringBuffer.WriteString(conn.username)
stringBuffer.WriteString(":") stringBuffer.WriteString(":")
stringBuffer.WriteString(conn.password) stringBuffer.WriteString(conn.password)
@ -122,26 +121,26 @@ func (conn *Connection) assembleURL(apiOp apiOperation, p peer) string {
stringBuffer.WriteString(p.port) stringBuffer.WriteString(p.port)
switch apiOp { switch apiOp {
case api_STATUS: case api_STATUS:
stringBuffer.WriteString("/status") stringBuffer.WriteString("/status")
case api_QUERY: case api_QUERY:
stringBuffer.WriteString("/db/query") stringBuffer.WriteString("/db/query")
case api_WRITE: case api_WRITE:
stringBuffer.WriteString("/db/execute") stringBuffer.WriteString("/db/execute")
} }
if ( apiOp == api_QUERY || apiOp == api_WRITE ) { if apiOp == api_QUERY || apiOp == api_WRITE {
stringBuffer.WriteString("?timings&transaction&level=") stringBuffer.WriteString("?timings&transaction&level=")
stringBuffer.WriteString(consistencyLevelNames[conn.consistencyLevel]) stringBuffer.WriteString(consistencyLevelNames[conn.consistencyLevel])
} }
switch apiOp { switch apiOp {
case api_QUERY: case api_QUERY:
trace("%s: assembled URL for an api_QUERY: %s",conn.ID,stringBuffer.String()) trace("%s: assembled URL for an api_QUERY: %s", conn.ID, stringBuffer.String())
case api_STATUS: case api_STATUS:
trace("%s: assembled URL for an api_STATUS: %s",conn.ID,stringBuffer.String()) trace("%s: assembled URL for an api_STATUS: %s", conn.ID, stringBuffer.String())
case api_WRITE: case api_WRITE:
trace("%s: assembled URL for an api_WRITE: %s",conn.ID,stringBuffer.String()) trace("%s: assembled URL for an api_WRITE: %s", conn.ID, stringBuffer.String())
} }
return stringBuffer.String() return stringBuffer.String()
@ -160,22 +159,26 @@ func (conn *Connection) assembleURL(apiOp apiOperation, p peer) string {
* *****************************************************************/ * *****************************************************************/
func (conn *Connection) updateClusterInfo() error { func (conn *Connection) updateClusterInfo() error {
trace("%s: updateClusterInfo() called",conn.ID) trace("%s: updateClusterInfo() called", conn.ID)
// start with a fresh new cluster // start with a fresh new cluster
var rc rqliteCluster var rc rqliteCluster
rc.conn = conn rc.conn = conn
responseBody, err := conn.rqliteApiGet(api_STATUS) responseBody, err := conn.rqliteApiGet(api_STATUS)
if ( err != nil ) { return err } if err != nil {
trace("%s: updateClusterInfo() back from api call OK",conn.ID) return err
}
trace("%s: updateClusterInfo() back from api call OK", conn.ID)
sections := make(map[string]interface{}) sections := make(map[string]interface{})
err = json.Unmarshal(responseBody,&sections) err = json.Unmarshal(responseBody, &sections)
if ( err != nil ) { return err } if err != nil {
return err
}
sMap := sections["store"].(map[string]interface{}) sMap := sections["store"].(map[string]interface{})
leaderRaftAddr := sMap["leader"].(string) leaderRaftAddr := sMap["leader"].(string)
trace("%s: leader from store section is %s",conn.ID,leaderRaftAddr) trace("%s: leader from store section is %s", conn.ID, leaderRaftAddr)
// leader in this case is the RAFT address // leader in this case is the RAFT address
// we want the HTTP address, so we'll use this as // we want the HTTP address, so we'll use this as
@ -185,32 +188,32 @@ func (conn *Connection) updateClusterInfo() error {
apiPeers := meta["APIPeers"].(map[string]interface{}) apiPeers := meta["APIPeers"].(map[string]interface{})
for raftAddr, httpAddr := range apiPeers { for raftAddr, httpAddr := range apiPeers {
trace("%s: examining httpAddr %s",conn.ID,httpAddr) trace("%s: examining httpAddr %s", conn.ID, httpAddr)
/* httpAddr are usually hostname:port */ /* httpAddr are usually hostname:port */
var p peer var p peer
parts := strings.Split(httpAddr.(string),":") parts := strings.Split(httpAddr.(string), ":")
p.hostname = parts[0] p.hostname = parts[0]
p.port = parts[1] p.port = parts[1]
// so is this the leader? // so is this the leader?
if ( leaderRaftAddr == raftAddr ) { if leaderRaftAddr == raftAddr {
trace ("%s: found leader at %s",conn.ID,httpAddr) trace("%s: found leader at %s", conn.ID, httpAddr)
rc.leader = p rc.leader = p
} else { } else {
rc.otherPeers = append(rc.otherPeers, p) rc.otherPeers = append(rc.otherPeers, p)
} }
} }
if ( rc.leader.hostname == "" ) { if rc.leader.hostname == "" {
return errors.New("could not determine leader from API status call") return errors.New("could not determine leader from API status call")
} }
// dump to trace // dump to trace
trace("%s: here is my cluster config:",conn.ID) trace("%s: here is my cluster config:", conn.ID)
trace("%s: leader : %s",conn.ID,rc.leader.String()) trace("%s: leader : %s", conn.ID, rc.leader.String())
for n, v := range rc.otherPeers { for n, v := range rc.otherPeers {
trace("%s: otherPeer #%d: %s",conn.ID,n,v.String()) trace("%s: otherPeer #%d: %s", conn.ID, n, v.String())
} }
// now make it official // now make it official
@ -218,4 +221,3 @@ func (conn *Connection) updateClusterInfo() error {
return nil return nil
} }

View File

@ -6,34 +6,33 @@ import "os"
func TestInitCluster(t *testing.T) { func TestInitCluster(t *testing.T) {
TraceOn(os.Stderr) TraceOn(os.Stderr)
t.Logf("trying Open") t.Logf("trying Open")
conn, err := Open(testUrl()) conn, err := Open(testUrl())
if ( err != nil ) { if err != nil {
t.Logf("--> FAILED") t.Logf("--> FAILED")
t.Fatal(err) t.Fatal(err)
} }
l, err := conn.Leader() l, err := conn.Leader()
if ( err != nil ) { if err != nil {
t.Logf("--> FAILED") t.Logf("--> FAILED")
t.Fail() t.Fail()
} }
if ( len(l) < 1 ) { if len(l) < 1 {
t.Logf("--> FAILED") t.Logf("--> FAILED")
t.Fail() t.Fail()
} }
p, err := conn.Peers() p, err := conn.Peers()
if ( err != nil ) { if err != nil {
t.Logf("--> FAILED") t.Logf("--> FAILED")
t.Fail() t.Fail()
} }
if ( len(p) < 1 ) { if len(p) < 1 {
t.Logf("--> FAILED") t.Logf("--> FAILED")
t.Fail() t.Fail()
} }
} }

105
conn.go
View File

@ -49,23 +49,22 @@ var wantsTrace bool
*/ */
type Connection struct { type Connection struct {
cluster rqliteCluster cluster rqliteCluster
/* /*
name type default name type default
*/ */
username string // username or "" username string // username or ""
password string // username or "" password string // username or ""
consistencyLevel consistencyLevel // WEAK consistencyLevel consistencyLevel // WEAK
wantsHTTPS bool // false unless connection URL is https wantsHTTPS bool // false unless connection URL is https
// variables below this line need to be initialized in Open() // variables below this line need to be initialized in Open()
timeout int // 10 timeout int // 10
hasBeenClosed bool // false hasBeenClosed bool // false
ID string // generated in init() ID string // generated in init()
} }
/* ***************************************************************** /* *****************************************************************
@ -76,7 +75,7 @@ type Connection struct {
func (conn *Connection) Close() { func (conn *Connection) Close() {
conn.hasBeenClosed = true conn.hasBeenClosed = true
trace("%s: %s",conn.ID,"closing connection") trace("%s: %s", conn.ID, "closing connection")
} }
/* ***************************************************************** /* *****************************************************************
@ -86,7 +85,7 @@ func (conn *Connection) Close() {
* *****************************************************************/ * *****************************************************************/
func (conn *Connection) ConsistencyLevel() (string, error) { func (conn *Connection) ConsistencyLevel() (string, error) {
if ( conn.hasBeenClosed) { if conn.hasBeenClosed {
return "", errClosed return "", errClosed
} }
return consistencyLevelNames[conn.consistencyLevel], nil return consistencyLevelNames[conn.consistencyLevel], nil
@ -99,16 +98,16 @@ func (conn *Connection) ConsistencyLevel() (string, error) {
* *****************************************************************/ * *****************************************************************/
func (conn *Connection) Leader() (string, error) { func (conn *Connection) Leader() (string, error) {
if ( conn.hasBeenClosed) { if conn.hasBeenClosed {
return "", errClosed return "", errClosed
} }
trace("%s: Leader(), calling updateClusterInfo()",conn.ID) trace("%s: Leader(), calling updateClusterInfo()", conn.ID)
err := conn.updateClusterInfo() err := conn.updateClusterInfo()
if ( err != nil ) { if err != nil {
trace("%s: Leader() got error from updateClusterInfo(): %s",conn.ID,err.Error()) trace("%s: Leader() got error from updateClusterInfo(): %s", conn.ID, err.Error())
return "", err return "", err
} else { } else {
trace("%s: Leader(), updateClusterInfo() OK",conn.ID) trace("%s: Leader(), updateClusterInfo() OK", conn.ID)
} }
return conn.cluster.leader.String(), nil return conn.cluster.leader.String(), nil
} }
@ -120,23 +119,23 @@ func (conn *Connection) Leader() (string, error) {
* *****************************************************************/ * *****************************************************************/
func (conn *Connection) Peers() ([]string, error) { func (conn *Connection) Peers() ([]string, error) {
if ( conn.hasBeenClosed) { if conn.hasBeenClosed {
var ans []string var ans []string
return ans, errClosed return ans, errClosed
} }
plist := make ([]string,0) plist := make([]string, 0)
trace("%s: Peers(), calling updateClusterInfo()",conn.ID) trace("%s: Peers(), calling updateClusterInfo()", conn.ID)
err := conn.updateClusterInfo() err := conn.updateClusterInfo()
if ( err != nil ) { if err != nil {
trace("%s: Peers() got error from updateClusterInfo(): %s",conn.ID,err.Error()) trace("%s: Peers() got error from updateClusterInfo(): %s", conn.ID, err.Error())
return plist, err return plist, err
} else { } else {
trace("%s: Peers(), updateClusterInfo() OK",conn.ID) trace("%s: Peers(), updateClusterInfo() OK", conn.ID)
} }
plist = append(plist,conn.cluster.leader.String()) plist = append(plist, conn.cluster.leader.String())
for _, p := range conn.cluster.otherPeers { for _, p := range conn.cluster.otherPeers {
plist = append(plist,p.String()) plist = append(plist, p.String())
} }
return plist, nil return plist, nil
} }
@ -148,15 +147,15 @@ func (conn *Connection) Peers() ([]string, error) {
* *****************************************************************/ * *****************************************************************/
func (conn *Connection) SetConsistencyLevel(levelDesired string) error { func (conn *Connection) SetConsistencyLevel(levelDesired string) error {
if ( conn.hasBeenClosed) { if conn.hasBeenClosed {
return errClosed return errClosed
} }
_, ok := consistencyLevels[levelDesired] _, ok := consistencyLevels[levelDesired]
if ( ok ) { if ok {
conn.consistencyLevel = consistencyLevels[levelDesired] conn.consistencyLevel = consistencyLevels[levelDesired]
return nil return nil
} }
return errors.New(fmt.Sprintf("unknown consistency level: %s",levelDesired)) return errors.New(fmt.Sprintf("unknown consistency level: %s", levelDesired))
} }
/* ***************************************************************** /* *****************************************************************
@ -204,21 +203,21 @@ func (conn *Connection) initConnection(url string) error {
// do some sanity checks. You know users. // do some sanity checks. You know users.
if ( len(url) < 7 ) { if len(url) < 7 {
return errors.New("url specified is impossibly short") return errors.New("url specified is impossibly short")
} }
if ( strings.HasPrefix(url,"http") == false ) { if strings.HasPrefix(url, "http") == false {
return errors.New("url does not start with 'http'") return errors.New("url does not start with 'http'")
} }
u, err := nurl.Parse(url) u, err := nurl.Parse(url)
if ( err != nil ) { if err != nil {
return err return err
} }
trace("%s: net.url.Parse() OK",conn.ID) trace("%s: net.url.Parse() OK", conn.ID)
if ( u.Scheme == "https" ) { if u.Scheme == "https" {
conn.wantsHTTPS = true conn.wantsHTTPS = true
} }
@ -233,27 +232,27 @@ func (conn *Connection) initConnection(url string) error {
// not guaranteed, so test if set // not guaranteed, so test if set
pass, isset := u.User.Password() pass, isset := u.User.Password()
if ( isset ) { if isset {
conn.password = pass conn.password = pass
} else { } else {
conn.password = "" conn.password = ""
} }
} }
if ( u.Host == "" ) { if u.Host == "" {
conn.cluster.leader.hostname = "localhost" conn.cluster.leader.hostname = "localhost"
} else { } else {
conn.cluster.leader.hostname = u.Host conn.cluster.leader.hostname = u.Host
} }
if ( u.Host == "" ) { if u.Host == "" {
conn.cluster.leader.hostname = "localhost" conn.cluster.leader.hostname = "localhost"
conn.cluster.leader.port = "4001" conn.cluster.leader.port = "4001"
} else { } else {
// SplitHostPort() should only return an error if there is no host port. // SplitHostPort() should only return an error if there is no host port.
// I think. // I think.
h, p, err := net.SplitHostPort(u.Host) h, p, err := net.SplitHostPort(u.Host)
if ( err != nil ) { if err != nil {
conn.cluster.leader.hostname = u.Host conn.cluster.leader.hostname = u.Host
} else { } else {
conn.cluster.leader.hostname = h conn.cluster.leader.hostname = h
@ -263,41 +262,39 @@ func (conn *Connection) initConnection(url string) error {
/* /*
at the moment, the only allowed query is "level=" with at the moment, the only allowed query is "level=" with
the desired consistency level the desired consistency level
*/ */
// default // default
conn.consistencyLevel = cl_WEAK conn.consistencyLevel = cl_WEAK
if ( u.RawQuery != "" ) { if u.RawQuery != "" {
if ( u.RawQuery == "level=weak") { if u.RawQuery == "level=weak" {
// that's ok but nothing to do // that's ok but nothing to do
} else if ( u.RawQuery == "level=strong" ) { } else if u.RawQuery == "level=strong" {
conn.consistencyLevel = cl_STRONG conn.consistencyLevel = cl_STRONG
} else if ( u.RawQuery == "level=none" ) { // the fools! } else if u.RawQuery == "level=none" { // the fools!
conn.consistencyLevel = cl_NONE conn.consistencyLevel = cl_NONE
} else { } else {
return errors.New("don't know what to do with this query: " + u.RawQuery) return errors.New("don't know what to do with this query: " + u.RawQuery)
} }
} }
trace("%s: parseDefaultPeer() is done:",conn.ID) trace("%s: parseDefaultPeer() is done:", conn.ID)
if ( conn.wantsHTTPS == true ) { if conn.wantsHTTPS == true {
trace("%s: %s -> %s",conn.ID,"wants https?","yes") trace("%s: %s -> %s", conn.ID, "wants https?", "yes")
} else { } else {
trace("%s: %s -> %s",conn.ID,"wants https?","no") trace("%s: %s -> %s", conn.ID, "wants https?", "no")
} }
trace("%s: %s -> %s",conn.ID,"username",conn.username) trace("%s: %s -> %s", conn.ID, "username", conn.username)
trace("%s: %s -> %s",conn.ID,"password",conn.password) trace("%s: %s -> %s", conn.ID, "password", conn.password)
trace("%s: %s -> %s",conn.ID,"hostname",conn.cluster.leader.hostname) trace("%s: %s -> %s", conn.ID, "hostname", conn.cluster.leader.hostname)
trace("%s: %s -> %s",conn.ID,"port",conn.cluster.leader.port) trace("%s: %s -> %s", conn.ID, "port", conn.cluster.leader.port)
trace("%s: %s -> %s",conn.ID,"consistencyLevel",consistencyLevelNames[conn.consistencyLevel]) trace("%s: %s -> %s", conn.ID, "consistencyLevel", consistencyLevelNames[conn.consistencyLevel])
conn.cluster.conn = conn conn.cluster.conn = conn
return nil return nil
} }

View File

@ -10,7 +10,7 @@
Learn more about rqlite at: https://github.com/rqlite/rqlite Learn more about rqlite at: https://github.com/rqlite/rqlite
*/ */
package gorqlite package gorqlite
/* /*
@ -33,16 +33,19 @@ import "strings"
* *****************************************************************/ * *****************************************************************/
type consistencyLevel int type consistencyLevel int
const ( const (
cl_NONE consistencyLevel = iota cl_NONE consistencyLevel = iota
cl_WEAK cl_WEAK
cl_STRONG cl_STRONG
) )
// used in several places, actually // used in several places, actually
var consistencyLevelNames map[consistencyLevel]string var consistencyLevelNames map[consistencyLevel]string
var consistencyLevels map[string]consistencyLevel var consistencyLevels map[string]consistencyLevel
type apiOperation int type apiOperation int
const ( const (
api_QUERY apiOperation = iota api_QUERY apiOperation = iota
api_STATUS api_STATUS
@ -56,7 +59,7 @@ const (
* *****************************************************************/ * *****************************************************************/
func init() { func init() {
traceOut = ioutil.Discard traceOut = ioutil.Discard
consistencyLevelNames = make(map[consistencyLevel]string) consistencyLevelNames = make(map[consistencyLevel]string)
consistencyLevelNames[cl_NONE] = "none" consistencyLevelNames[cl_NONE] = "none"
@ -70,7 +73,6 @@ func init() {
} }
/* ***************************************************************** /* *****************************************************************
Open() creates and returns a "connection" to rqlite. Open() creates and returns a "connection" to rqlite.
@ -99,7 +101,7 @@ func Open(connURL string) (Connection, error) {
return conn, err return conn, err
} }
conn.ID = fmt.Sprintf("%X-%X-%X-%X-%X", b[0:4], b[4:6], b[6:8], b[8:10], b[10:]) conn.ID = fmt.Sprintf("%X-%X-%X-%X-%X", b[0:4], b[4:6], b[6:8], b[8:10], b[10:])
trace("%s: Open() called for url: %s",conn.ID,connURL) trace("%s: Open() called for url: %s", conn.ID, connURL)
// set defaults // set defaults
conn.timeout = 10 conn.timeout = 10
@ -107,7 +109,7 @@ func Open(connURL string) (Connection, error) {
// parse the URL given // parse the URL given
err = conn.initConnection(connURL) err = conn.initConnection(connURL)
if ( err != nil ) { if err != nil {
return conn, err return conn, err
} }
@ -146,7 +148,7 @@ func Open(connURL string) (Connection, error) {
func trace(pattern string, args ...interface{}) { func trace(pattern string, args ...interface{}) {
// don't do the probably expensive Sprintf() if not needed // don't do the probably expensive Sprintf() if not needed
if ( wantsTrace == false ) { if wantsTrace == false {
return return
} }
@ -156,8 +158,8 @@ func trace(pattern string, args ...interface{}) {
// make sure there is one and only one newline // make sure there is one and only one newline
nlPattern := strings.TrimSpace(pattern) + "\n" nlPattern := strings.TrimSpace(pattern) + "\n"
msg := fmt.Sprintf(nlPattern,args...) msg := fmt.Sprintf(nlPattern, args...)
traceOut.Write( []byte( msg ) ) traceOut.Write([]byte(msg))
} }
/* /*

119
query.go
View File

@ -91,12 +91,12 @@ QueryOne() is a convenience method that wraps Query() into a single-statement
method. method.
*/ */
func (conn *Connection) QueryOne(sqlStatement string) (qr QueryResult, err error) { func (conn *Connection) QueryOne(sqlStatement string) (qr QueryResult, err error) {
if ( conn.hasBeenClosed) { if conn.hasBeenClosed {
qr.Err = errClosed qr.Err = errClosed
return qr, errClosed return qr, errClosed
} }
sqlStatements := make([]string,0) sqlStatements := make([]string, 0)
sqlStatements = append(sqlStatements,sqlStatement) sqlStatements = append(sqlStatements, sqlStatement)
qra, err := conn.Query(sqlStatements) qra, err := conn.Query(sqlStatements)
return qra[0], err return qra[0], err
} }
@ -107,35 +107,35 @@ Query() is used to perform SELECT operations in the database.
It takes an array of SQL statements and executes them in a single transaction, returning an array of QueryResult vars. It takes an array of SQL statements and executes them in a single transaction, returning an array of QueryResult vars.
*/ */
func (conn *Connection) Query(sqlStatements []string) (results []QueryResult, err error) { func (conn *Connection) Query(sqlStatements []string) (results []QueryResult, err error) {
results = make([]QueryResult,0) results = make([]QueryResult, 0)
if ( conn.hasBeenClosed) { if conn.hasBeenClosed {
var errResult QueryResult var errResult QueryResult
errResult.Err = errClosed errResult.Err = errClosed
results = append(results,errResult) results = append(results, errResult)
return results, errClosed return results, errClosed
} }
trace("%s: Query() for %d statements",conn.ID,len(sqlStatements)) trace("%s: Query() for %d statements", conn.ID, len(sqlStatements))
// if we get an error POSTing, that's a showstopper // if we get an error POSTing, that's a showstopper
response, err := conn.rqliteApiPost(api_QUERY,sqlStatements) response, err := conn.rqliteApiPost(api_QUERY, sqlStatements)
if ( err != nil ) { if err != nil {
trace("%s: rqliteApiCall() ERROR: %s",conn.ID,err.Error()) trace("%s: rqliteApiCall() ERROR: %s", conn.ID, err.Error())
var errResult QueryResult var errResult QueryResult
errResult.Err = err errResult.Err = err
results = append(results,errResult) results = append(results, errResult)
return results, err return results, err
} }
trace("%s: rqliteApiCall() OK",conn.ID) trace("%s: rqliteApiCall() OK", conn.ID)
// if we get an error Unmarshaling, that's a showstopper // if we get an error Unmarshaling, that's a showstopper
var sections map[string]interface{} var sections map[string]interface{}
err = json.Unmarshal(response,&sections) err = json.Unmarshal(response, &sections)
if ( err != nil ) { if err != nil {
trace("%s: json.Unmarshal() ERROR: %s",conn.ID,err.Error()) trace("%s: json.Unmarshal() ERROR: %s", conn.ID, err.Error())
var errResult QueryResult var errResult QueryResult
errResult.Err = err errResult.Err = err
results = append(results,errResult) results = append(results, errResult)
return results, err return results, err
} }
@ -145,11 +145,11 @@ func (conn *Connection) Query(sqlStatements []string) (results []QueryResult, er
*/ */
resultsArray := sections["results"].([]interface{}) resultsArray := sections["results"].([]interface{})
trace("%s: I have %d result(s) to parse",conn.ID,len(resultsArray)) trace("%s: I have %d result(s) to parse", conn.ID, len(resultsArray))
numStatementErrors := 0 numStatementErrors := 0
for n, r := range resultsArray { for n, r := range resultsArray {
trace("%s: parsing result %d",conn.ID,n) trace("%s: parsing result %d", conn.ID, n)
var thisQR QueryResult var thisQR QueryResult
thisQR.conn = conn thisQR.conn = conn
@ -159,9 +159,9 @@ func (conn *Connection) Query(sqlStatements []string) (results []QueryResult, er
// did we get an error? // did we get an error?
_, ok := thisResult["error"] _, ok := thisResult["error"]
if ok { if ok {
trace("%s: have an error on this result: %s",conn.ID,thisResult["error"].(string)) trace("%s: have an error on this result: %s", conn.ID, thisResult["error"].(string))
thisQR.Err = errors.New(thisResult["error"].(string)) thisQR.Err = errors.New(thisResult["error"].(string))
results = append(results,thisQR) results = append(results, thisQR)
numStatementErrors++ numStatementErrors++
continue continue
} }
@ -172,28 +172,28 @@ func (conn *Connection) Query(sqlStatements []string) (results []QueryResult, er
// column & type are an array of strings // column & type are an array of strings
c := thisResult["columns"].([]interface{}) c := thisResult["columns"].([]interface{})
t := thisResult["types"].([]interface{}) t := thisResult["types"].([]interface{})
for i := 0; i < len(c) ; i++ { for i := 0; i < len(c); i++ {
thisQR.columns = append(thisQR.columns,c[i].(string)) thisQR.columns = append(thisQR.columns, c[i].(string))
thisQR.types = append(thisQR.types,t[i].(string)) thisQR.types = append(thisQR.types, t[i].(string))
} }
// and values are an array of arrays // and values are an array of arrays
if ( thisResult["values"] != nil ) { if thisResult["values"] != nil {
thisQR.values = thisResult["values"].([]interface{}) thisQR.values = thisResult["values"].([]interface{})
} else { } else {
trace("%s: fyi, no values this query",conn.ID) trace("%s: fyi, no values this query", conn.ID)
} }
thisQR.rowNumber = -1 thisQR.rowNumber = -1
trace("%s: this result (#col,time) %d %f",conn.ID,len(thisQR.columns),thisQR.Timing) trace("%s: this result (#col,time) %d %f", conn.ID, len(thisQR.columns), thisQR.Timing)
results = append(results,thisQR) results = append(results, thisQR)
} }
trace("%s: finished parsing, returning %d results",conn.ID,len(results)) trace("%s: finished parsing, returning %d results", conn.ID, len(results))
if ( numStatementErrors > 0 ) { if numStatementErrors > 0 {
return results, errors.New(fmt.Sprintf("there were %d statement errors",numStatementErrors)) return results, errors.New(fmt.Sprintf("there were %d statement errors", numStatementErrors))
} else { } else {
return results, nil return results, nil
} }
@ -217,12 +217,12 @@ then a QueryResult would hold any errors from that query, a list of columns and
Query() returns an array of QueryResult vars, while QueryOne() returns a single variable. Query() returns an array of QueryResult vars, while QueryOne() returns a single variable.
*/ */
type QueryResult struct { type QueryResult struct {
conn *Connection conn *Connection
Err error Err error
columns []string columns []string
types []string types []string
Timing float64 Timing float64
values []interface{} values []interface{}
rowNumber int64 rowNumber int64
} }
@ -261,12 +261,12 @@ func (qr *QueryResult) Map() (map[string]interface{}, error) {
trace("%s: Map() called for row %d", qr.conn.ID, qr.rowNumber) trace("%s: Map() called for row %d", qr.conn.ID, qr.rowNumber)
ans := make(map[string]interface{}) ans := make(map[string]interface{})
if ( qr.rowNumber == -1 ) { if qr.rowNumber == -1 {
return ans, errors.New("you need to Next() before you Map(), sorry, it's complicated") return ans, errors.New("you need to Next() before you Map(), sorry, it's complicated")
} }
thisRowValues := qr.values[qr.rowNumber].([]interface {}) thisRowValues := qr.values[qr.rowNumber].([]interface{})
for i := 0; i<len(qr.columns); i++ { for i := 0; i < len(qr.columns); i++ {
ans[qr.columns[i]] = thisRowValues[i] ans[qr.columns[i]] = thisRowValues[i]
} }
@ -293,7 +293,7 @@ A common idiom:
} }
*/ */
func (qr *QueryResult) Next() bool { func (qr *QueryResult) Next() bool {
if ( qr.rowNumber >= int64(len(qr.values) - 1 )) { if qr.rowNumber >= int64(len(qr.values)-1) {
return false return false
} }
@ -346,31 +346,31 @@ are a subset of the types JSON uses:
booleans, JSON arrays, and JSON objects are not supported, booleans, JSON arrays, and JSON objects are not supported,
since sqlite does not support them. since sqlite does not support them.
*/ */
func (qr *QueryResult) Scan(dest... interface{}) error { func (qr *QueryResult) Scan(dest ...interface{}) error {
trace("%s: Scan() called for %d vars", qr.conn.ID,len(dest)) trace("%s: Scan() called for %d vars", qr.conn.ID, len(dest))
if ( qr.rowNumber == -1 ) { if qr.rowNumber == -1 {
return errors.New("you need to Next() before you Scan(), sorry, it's complicated") return errors.New("you need to Next() before you Scan(), sorry, it's complicated")
} }
if ( len(dest) != len(qr.columns) ) { if len(dest) != len(qr.columns) {
return errors.New(fmt.Sprintf("expected %d columns but got %d vars\n", len(qr.columns), len(dest))) return errors.New(fmt.Sprintf("expected %d columns but got %d vars\n", len(qr.columns), len(dest)))
} }
thisRowValues := qr.values[qr.rowNumber].([]interface {}) thisRowValues := qr.values[qr.rowNumber].([]interface{})
for n, d := range dest { for n, d := range dest {
switch d.(type) { switch d.(type) {
case *int64: case *int64:
f := int64(thisRowValues[n].(float64)) f := int64(thisRowValues[n].(float64))
*d.(*int64) = f *d.(*int64) = f
case *float64: case *float64:
f := float64(thisRowValues[n].(float64)) f := float64(thisRowValues[n].(float64))
*d.(*float64) = f *d.(*float64) = f
case *string: case *string:
s := string(thisRowValues[n].(string)) s := string(thisRowValues[n].(string))
*d.(*string) = s *d.(*string) = s
default: default:
return errors.New(fmt.Sprintf("unknown destination type to scan into in variable #%d",n)) return errors.New(fmt.Sprintf("unknown destination type to scan into in variable #%d", n))
} }
} }
@ -393,4 +393,3 @@ This info may additionally conflict with the reality that your data is being JSO
func (qr *QueryResult) Types() []string { func (qr *QueryResult) Types() []string {
return qr.types return qr.types
} }

View File

@ -2,7 +2,7 @@ package gorqlite
import "testing" import "testing"
func TestQueryOne (t *testing.T) { func TestQueryOne(t *testing.T) {
var wr WriteResult var wr WriteResult
var qr QueryResult var qr QueryResult
var wResults []WriteResult var wResults []WriteResult
@ -11,59 +11,59 @@ func TestQueryOne (t *testing.T) {
t.Logf("trying Open") t.Logf("trying Open")
conn, err := Open(testUrl()) conn, err := Open(testUrl())
if ( err != nil ) { if err != nil {
t.Logf("--> FATAL") t.Logf("--> FATAL")
t.Fatal() t.Fatal()
} }
t.Logf("trying WriteOne DROP") t.Logf("trying WriteOne DROP")
wr, err = conn.WriteOne("DROP TABLE IF EXISTS " + testTableName()) wr, err = conn.WriteOne("DROP TABLE IF EXISTS " + testTableName())
if ( err != nil ) { if err != nil {
t.Logf("--> FATAL") t.Logf("--> FATAL")
t.Fatal() t.Fatal()
} }
t.Logf("trying WriteOne CREATE") t.Logf("trying WriteOne CREATE")
wr, err = conn.WriteOne("CREATE TABLE " + testTableName() + " (id integer, name text)") wr, err = conn.WriteOne("CREATE TABLE " + testTableName() + " (id integer, name text)")
if ( err != nil ) { if err != nil {
t.Logf("--> FATAL") t.Logf("--> FATAL")
t.Fatal() t.Fatal()
} }
t.Logf("trying Write INSERT") t.Logf("trying Write INSERT")
s := make([]string,0) s := make([]string, 0)
s = append(s,"INSERT INTO " + testTableName() + " (id, name) VALUES ( 1, 'Romulan' )") s = append(s, "INSERT INTO "+testTableName()+" (id, name) VALUES ( 1, 'Romulan' )")
s = append(s,"INSERT INTO " + testTableName() + " (id, name) VALUES ( 2, 'Vulcan' )") s = append(s, "INSERT INTO "+testTableName()+" (id, name) VALUES ( 2, 'Vulcan' )")
s = append(s,"INSERT INTO " + testTableName() + " (id, name) VALUES ( 3, 'Klingon' )") s = append(s, "INSERT INTO "+testTableName()+" (id, name) VALUES ( 3, 'Klingon' )")
s = append(s,"INSERT INTO " + testTableName() + " (id, name) VALUES ( 4, 'Ferengi' )") s = append(s, "INSERT INTO "+testTableName()+" (id, name) VALUES ( 4, 'Ferengi' )")
s = append(s,"INSERT INTO " + testTableName() + " (id, name) VALUES ( 5, 'Cardassian' )") s = append(s, "INSERT INTO "+testTableName()+" (id, name) VALUES ( 5, 'Cardassian' )")
wResults, err = conn.Write(s) wResults, err = conn.Write(s)
if ( err != nil ) { if err != nil {
t.Logf("--> FATAL") t.Logf("--> FATAL")
t.Fatal() t.Fatal()
} }
t.Logf("trying QueryOne") t.Logf("trying QueryOne")
qr, err = conn.QueryOne("SELECT name FROM " + testTableName() + " WHERE id > 3") qr, err = conn.QueryOne("SELECT name FROM " + testTableName() + " WHERE id > 3")
if ( err != nil ) { if err != nil {
t.Logf("--> FAILED") t.Logf("--> FAILED")
t.Fail() t.Fail()
} }
t.Logf("trying Next()") t.Logf("trying Next()")
na := qr.Next() na := qr.Next()
if ( na != true ) { if na != true {
t.Logf("--> FAILED") t.Logf("--> FAILED")
t.Fail() t.Fail()
} }
t.Logf("trying Map()") t.Logf("trying Map()")
r, err := qr.Map() r, err := qr.Map()
if ( err != nil ) { if err != nil {
t.Logf("--> FAILED") t.Logf("--> FAILED")
t.Fail() t.Fail()
} }
if ( r["name"].(string) != "Ferengi" ) { if r["name"].(string) != "Ferengi" {
t.Logf("--> FAILED") t.Logf("--> FAILED")
t.Fail() t.Fail()
} }
@ -71,30 +71,30 @@ func TestQueryOne (t *testing.T) {
t.Logf("trying Scan(), also float64->int64 in Scan()") t.Logf("trying Scan(), also float64->int64 in Scan()")
var id int64 var id int64
var name string var name string
err = qr.Scan(&id,&name) err = qr.Scan(&id, &name)
if ( err == nil ) { if err == nil {
t.Logf("--> FAILED (%s)",err.Error()) t.Logf("--> FAILED (%s)", err.Error())
t.Fail() t.Fail()
} }
err = qr.Scan(&name) err = qr.Scan(&name)
if ( err != nil ) { if err != nil {
t.Logf("--> FAILED (%s)",err.Error()) t.Logf("--> FAILED (%s)", err.Error())
t.Fail() t.Fail()
} }
if ( name != "Ferengi" ) { if name != "Ferengi" {
t.Logf("--> FAILED, name should be 'Ferengi' but it's '%s'",name) t.Logf("--> FAILED, name should be 'Ferengi' but it's '%s'", name)
t.Fail() t.Fail()
} }
qr.Next() qr.Next()
err = qr.Scan(&name) err = qr.Scan(&name)
if ( name != "Cardassian" ) { if name != "Cardassian" {
t.Logf("--> FAILED") t.Logf("--> FAILED")
t.Fail() t.Fail()
} }
t.Logf("trying WriteOne DROP") t.Logf("trying WriteOne DROP")
wr, err = conn.WriteOne("DROP TABLE IF EXISTS " + testTableName() + "") wr, err = conn.WriteOne("DROP TABLE IF EXISTS " + testTableName() + "")
if ( err != nil ) { if err != nil {
t.Logf("--> FAILED") t.Logf("--> FAILED")
t.Fail() t.Fail()
} }
@ -104,18 +104,18 @@ func TestQueryOne (t *testing.T) {
t.Logf("trying WriteOne after Close") t.Logf("trying WriteOne after Close")
wr, err = conn.WriteOne("DROP TABLE IF EXISTS " + testTableName() + "") wr, err = conn.WriteOne("DROP TABLE IF EXISTS " + testTableName() + "")
if ( err == nil ) { if err == nil {
t.Logf("--> FAILED") t.Logf("--> FAILED")
t.Fail() t.Fail()
} }
_ = wr _ = wr
t.Logf("trying Write after Close") t.Logf("trying Write after Close")
t1 := make([]string,0) t1 := make([]string, 0)
t1 = append(t1,"DROP TABLE IF EXISTS " + testTableName() + "") t1 = append(t1, "DROP TABLE IF EXISTS "+testTableName()+"")
t1 = append(t1,"DROP TABLE IF EXISTS " + testTableName() + "") t1 = append(t1, "DROP TABLE IF EXISTS "+testTableName()+"")
wResults, err = conn.Write(t1) wResults, err = conn.Write(t1)
if ( err == nil ) { if err == nil {
t.Logf("--> FAILED") t.Logf("--> FAILED")
t.Fail() t.Fail()
} }
@ -123,24 +123,22 @@ func TestQueryOne (t *testing.T) {
t.Logf("trying QueryOne after Close") t.Logf("trying QueryOne after Close")
qr, err = conn.QueryOne("SELECT id FROM " + testTableName() + "") qr, err = conn.QueryOne("SELECT id FROM " + testTableName() + "")
if ( err == nil ) { if err == nil {
t.Logf("--> FAILED") t.Logf("--> FAILED")
t.Fail() t.Fail()
} }
_ = qr _ = qr
t.Logf("trying Query after Close") t.Logf("trying Query after Close")
t2 := make([]string,0) t2 := make([]string, 0)
t2 = append(t2,"SELECT id FROM " + testTableName() + "") t2 = append(t2, "SELECT id FROM "+testTableName()+"")
t2 = append(t2,"SELECT name FROM " + testTableName() + "") t2 = append(t2, "SELECT name FROM "+testTableName()+"")
t2 = append(t2,"SELECT id,name FROM " + testTableName() + "") t2 = append(t2, "SELECT id,name FROM "+testTableName()+"")
qResults, err = conn.Query(t2) qResults, err = conn.Query(t2)
if ( err == nil ) { if err == nil {
t.Logf("--> FAILED") t.Logf("--> FAILED")
t.Fail() t.Fail()
} }
_ = qResults _ = qResults
} }

View File

@ -8,7 +8,7 @@ import "os"
func testUrl() string { func testUrl() string {
url := os.Getenv("GORQLITE_TEST_URL") url := os.Getenv("GORQLITE_TEST_URL")
if ( url == "" ) { if url == "" {
url = "http://" url = "http://"
} }
return url return url
@ -16,10 +16,8 @@ func testUrl() string {
func testTableName() string { func testTableName() string {
tableName := os.Getenv("GORQLITE_TEST_TABLE") tableName := os.Getenv("GORQLITE_TEST_TABLE")
if ( tableName == "" ) { if tableName == "" {
tableName = "gorqlite_test" tableName = "gorqlite_test"
} }
return tableName return tableName
} }

View File

@ -58,13 +58,13 @@ method.
*/ */
func (conn *Connection) WriteOne(sqlStatement string) (wr WriteResult, err error) { func (conn *Connection) WriteOne(sqlStatement string) (wr WriteResult, err error) {
if ( conn.hasBeenClosed) { if conn.hasBeenClosed {
wr.Err = errClosed wr.Err = errClosed
return wr, errClosed return wr, errClosed
} }
sqlStatements := make([]string,0) sqlStatements := make([]string, 0)
sqlStatements = append(sqlStatements,sqlStatement) sqlStatements = append(sqlStatements, sqlStatement)
wra , err := conn.Write(sqlStatements) wra, err := conn.Write(sqlStatements)
return wra[0], err return wra[0], err
} }
@ -78,34 +78,34 @@ All statements are executed as a single transaction.
Write() returns an error if one is encountered during its operation. If it's something like a call to the rqlite API, then it'll return that error. If one statement out of several has an error, it will return a generic "there were %d statement errors" and you'll have to look at the individual statement's Err for more info. Write() returns an error if one is encountered during its operation. If it's something like a call to the rqlite API, then it'll return that error. If one statement out of several has an error, it will return a generic "there were %d statement errors" and you'll have to look at the individual statement's Err for more info.
*/ */
func (conn *Connection) Write(sqlStatements []string) (results []WriteResult, err error) { func (conn *Connection) Write(sqlStatements []string) (results []WriteResult, err error) {
results = make([]WriteResult,0) results = make([]WriteResult, 0)
if ( conn.hasBeenClosed) { if conn.hasBeenClosed {
var errResult WriteResult var errResult WriteResult
errResult.Err = errClosed errResult.Err = errClosed
results = append(results,errResult) results = append(results, errResult)
return results, errClosed return results, errClosed
} }
trace("%s: Write() for %d statements",conn.ID,len(sqlStatements)) trace("%s: Write() for %d statements", conn.ID, len(sqlStatements))
response, err := conn.rqliteApiPost(api_WRITE,sqlStatements) response, err := conn.rqliteApiPost(api_WRITE, sqlStatements)
if ( err != nil ) { if err != nil {
trace("%s: rqliteApiCall() ERROR: %s",conn.ID,err.Error()) trace("%s: rqliteApiCall() ERROR: %s", conn.ID, err.Error())
var errResult WriteResult var errResult WriteResult
errResult.Err = err errResult.Err = err
results = append(results,errResult) results = append(results, errResult)
return results, err return results, err
} }
trace("%s: rqliteApiCall() OK",conn.ID) trace("%s: rqliteApiCall() OK", conn.ID)
var sections map[string]interface{} var sections map[string]interface{}
err = json.Unmarshal(response,&sections) err = json.Unmarshal(response, &sections)
if ( err != nil ) { if err != nil {
trace("%s: json.Unmarshal() ERROR: %s",conn.ID,err.Error()) trace("%s: json.Unmarshal() ERROR: %s", conn.ID, err.Error())
var errResult WriteResult var errResult WriteResult
errResult.Err = err errResult.Err = err
results = append(results,errResult) results = append(results, errResult)
return results, err return results, err
} }
@ -115,10 +115,10 @@ func (conn *Connection) Write(sqlStatements []string) (results []WriteResult, er
*/ */
resultsArray := sections["results"].([]interface{}) resultsArray := sections["results"].([]interface{})
trace("%s: I have %d result(s) to parse",conn.ID,len(resultsArray)) trace("%s: I have %d result(s) to parse", conn.ID, len(resultsArray))
numStatementErrors := 0 numStatementErrors := 0
for n, k := range resultsArray { for n, k := range resultsArray {
trace("%s: starting on result %d",conn.ID,n) trace("%s: starting on result %d", conn.ID, n)
thisResult := k.(map[string]interface{}) thisResult := k.(map[string]interface{})
var thisWR WriteResult var thisWR WriteResult
@ -127,9 +127,9 @@ func (conn *Connection) Write(sqlStatements []string) (results []WriteResult, er
// did we get an error? // did we get an error?
_, ok := thisResult["error"] _, ok := thisResult["error"]
if ok { if ok {
trace("%s: have an error on this result: %s",conn.ID,thisResult["error"].(string)) trace("%s: have an error on this result: %s", conn.ID, thisResult["error"].(string))
thisWR.Err = errors.New(thisResult["error"].(string)) thisWR.Err = errors.New(thisResult["error"].(string))
results = append(results,thisWR) results = append(results, thisWR)
numStatementErrors += 1 numStatementErrors += 1
continue continue
} }
@ -145,14 +145,14 @@ func (conn *Connection) Write(sqlStatements []string) (results []WriteResult, er
} }
thisWR.Timing = thisResult["time"].(float64) thisWR.Timing = thisResult["time"].(float64)
trace("%s: this result (LII,RA,T): %d %d %f",conn.ID,thisWR.LastInsertID,thisWR.RowsAffected,thisWR.Timing) trace("%s: this result (LII,RA,T): %d %d %f", conn.ID, thisWR.LastInsertID, thisWR.RowsAffected, thisWR.Timing)
results = append(results,thisWR) results = append(results, thisWR)
} }
trace("%s: finished parsing, returning %d results",conn.ID,len(results)) trace("%s: finished parsing, returning %d results", conn.ID, len(results))
if ( numStatementErrors > 0 ) { if numStatementErrors > 0 {
return results, errors.New(fmt.Sprintf("there were %d statement errors",numStatementErrors)) return results, errors.New(fmt.Sprintf("there were %d statement errors", numStatementErrors))
} else { } else {
return results, nil return results, nil
} }
@ -170,10 +170,9 @@ A WriteResult holds the result of a single statement sent to Write().
Write() returns an array of WriteResult vars, while WriteOne() returns a single WriteResult. Write() returns an array of WriteResult vars, while WriteOne() returns a single WriteResult.
*/ */
type WriteResult struct { type WriteResult struct {
Err error // don't trust the rest if this isn't nil Err error // don't trust the rest if this isn't nil
Timing float64 Timing float64
RowsAffected int64 // affected by the change RowsAffected int64 // affected by the change
LastInsertID int64 // if relevant, otherwise zero value LastInsertID int64 // if relevant, otherwise zero value
conn *Connection conn *Connection
} }

View File

@ -1,108 +1,108 @@
package gorqlite package gorqlite
import "testing" import "testing"
// import "os" // import "os"
func TestWriteOne (t *testing.T) { func TestWriteOne(t *testing.T) {
var wr WriteResult var wr WriteResult
var err error var err error
t.Logf("trying Open") t.Logf("trying Open")
conn, err := Open(testUrl()) conn, err := Open(testUrl())
if ( err != nil ) { if err != nil {
t.Logf("--> FATAL") t.Logf("--> FATAL")
t.Fatal(err) t.Fatal(err)
} }
t.Logf("trying WriteOne DROP") t.Logf("trying WriteOne DROP")
wr, err = conn.WriteOne("DROP TABLE IF EXISTS " + testTableName() + "") wr, err = conn.WriteOne("DROP TABLE IF EXISTS " + testTableName() + "")
if ( err != nil ) { if err != nil {
t.Logf("--> FAILED") t.Logf("--> FAILED")
t.Fail() t.Fail()
} }
t.Logf("trying WriteOne CTHULHU (should fail, bad SQL)") t.Logf("trying WriteOne CTHULHU (should fail, bad SQL)")
wr, err = conn.WriteOne("CTHULHU") wr, err = conn.WriteOne("CTHULHU")
if ( err == nil ) { if err == nil {
t.Logf("--> FAILED") t.Logf("--> FAILED")
t.Fail() t.Fail()
} }
t.Logf("trying WriteOne CREATE") t.Logf("trying WriteOne CREATE")
wr, err = conn.WriteOne("CREATE TABLE " + testTableName() + " (id integer, name text)") wr, err = conn.WriteOne("CREATE TABLE " + testTableName() + " (id integer, name text)")
if ( err != nil ) { if err != nil {
t.Logf("--> FAILED") t.Logf("--> FAILED")
t.Fail() t.Fail()
} }
t.Logf("trying WriteOne INSERT") t.Logf("trying WriteOne INSERT")
wr, err = conn.WriteOne("INSERT INTO " + testTableName() + " (id, name) VALUES ( 1, 'aaa bbb ccc' )") wr, err = conn.WriteOne("INSERT INTO " + testTableName() + " (id, name) VALUES ( 1, 'aaa bbb ccc' )")
if ( err != nil ) { if err != nil {
t.Logf("--> FAILED") t.Logf("--> FAILED")
t.Fail() t.Fail()
} }
t.Logf("checking WriteOne RowsAffected") t.Logf("checking WriteOne RowsAffected")
if ( wr.RowsAffected != 1 ) { if wr.RowsAffected != 1 {
t.Logf("--> FAILED") t.Logf("--> FAILED")
t.Fail() t.Fail()
} }
t.Logf("trying WriteOne DROP") t.Logf("trying WriteOne DROP")
wr, err = conn.WriteOne("DROP TABLE IF EXISTS " + testTableName() + "") wr, err = conn.WriteOne("DROP TABLE IF EXISTS " + testTableName() + "")
if ( err != nil ) { if err != nil {
t.Logf("--> FAILED") t.Logf("--> FAILED")
t.Fail() t.Fail()
} }
} }
func TestWrite (t *testing.T) { func TestWrite(t *testing.T) {
var results []WriteResult var results []WriteResult
var err error var err error
var s []string var s []string
t.Logf("trying Open") t.Logf("trying Open")
conn, err := Open(testUrl()) conn, err := Open(testUrl())
if ( err != nil ) { if err != nil {
t.Logf("--> FATAL") t.Logf("--> FATAL")
t.Fatal(err) t.Fatal(err)
} }
t.Logf("trying Write DROP & CREATE") t.Logf("trying Write DROP & CREATE")
s = make([]string,0) s = make([]string, 0)
s = append(s, "DROP TABLE IF EXISTS " + testTableName() + "") s = append(s, "DROP TABLE IF EXISTS "+testTableName()+"")
s = append(s, "CREATE TABLE " + testTableName() + " (id integer, name text)") s = append(s, "CREATE TABLE "+testTableName()+" (id integer, name text)")
results, err = conn.Write(s) results, err = conn.Write(s)
if ( err != nil ) { if err != nil {
t.Logf("--> FAILED") t.Logf("--> FAILED")
t.Fail() t.Fail()
} }
t.Logf("trying Write INSERT") t.Logf("trying Write INSERT")
s = make([]string,0) s = make([]string, 0)
s = append (s, "INSERT INTO " + testTableName() + " (id, name) VALUES ( 1, 'aaa bbb ccc' )") s = append(s, "INSERT INTO "+testTableName()+" (id, name) VALUES ( 1, 'aaa bbb ccc' )")
s = append (s, "INSERT INTO " + testTableName() + " (id, name) VALUES ( 2, 'ddd eee fff' )") s = append(s, "INSERT INTO "+testTableName()+" (id, name) VALUES ( 2, 'ddd eee fff' )")
s = append (s, "INSERT INTO " + testTableName() + " (id, name) VALUES ( 3, 'ggg hhh iii' )") s = append(s, "INSERT INTO "+testTableName()+" (id, name) VALUES ( 3, 'ggg hhh iii' )")
s = append (s, "INSERT INTO " + testTableName() + " (id, name) VALUES ( 4, 'jjj kkk lll' )") s = append(s, "INSERT INTO "+testTableName()+" (id, name) VALUES ( 4, 'jjj kkk lll' )")
results, err = conn.Write(s) results, err = conn.Write(s)
if ( err != nil ) { if err != nil {
t.Logf("--> FAILED") t.Logf("--> FAILED")
t.Fail() t.Fail()
} }
if ( len(results) != 4 ) { if len(results) != 4 {
t.Logf("--> FAILED") t.Logf("--> FAILED")
t.Fail() t.Fail()
} }
t.Logf("trying Write DROP") t.Logf("trying Write DROP")
s = make([]string,0) s = make([]string, 0)
s = append(s, "DROP TABLE IF EXISTS " + testTableName() + "") s = append(s, "DROP TABLE IF EXISTS "+testTableName()+"")
results, err = conn.Write(s) results, err = conn.Write(s)
if ( err != nil ) { if err != nil {
t.Logf("--> FAILED") t.Logf("--> FAILED")
t.Fail() t.Fail()
} }
} }