run gofmt
This commit is contained in:
parent
edf576cd33
commit
40a5e952d2
129
api.go
129
api.go
|
@ -1,6 +1,6 @@
|
||||||
package gorqlite
|
package gorqlite
|
||||||
|
|
||||||
/*
|
/*
|
||||||
this file has low level stuff:
|
this file has low level stuff:
|
||||||
|
|
||||||
rqliteApiGet()
|
rqliteApiGet()
|
||||||
|
@ -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())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
92
cluster.go
92
cluster.go
|
@ -5,7 +5,7 @@ package gorqlite
|
||||||
|
|
||||||
types:
|
types:
|
||||||
peer
|
peer
|
||||||
rqliteCluster
|
rqliteCluster
|
||||||
Connection methods:
|
Connection methods:
|
||||||
assembleURL (from a peer)
|
assembleURL (from a peer)
|
||||||
updateClusterInfo (does the full cluster discovery via status)
|
updateClusterInfo (does the full cluster discovery via status)
|
||||||
|
@ -32,19 +32,18 @@ import "strings"
|
||||||
|
|
||||||
this is an internal type to abstact peer info.
|
this is an internal type to abstact peer info.
|
||||||
|
|
||||||
note that hostname is sometimes used for "has this struct been
|
note that hostname is sometimes used for "has this struct been
|
||||||
inialized" checks.
|
inialized" checks.
|
||||||
|
|
||||||
* *****************************************************************/
|
* *****************************************************************/
|
||||||
|
|
||||||
|
|
||||||
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,§ions)
|
err = json.Unmarshal(responseBody, §ions)
|
||||||
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,37 +188,36 @@ 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
|
||||||
conn.cluster = rc
|
conn.cluster = rc
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
119
conn.go
119
conn.go
|
@ -35,13 +35,13 @@ var wantsTrace bool
|
||||||
/*
|
/*
|
||||||
The connection abstraction. Note that since rqlite is stateless,
|
The connection abstraction. Note that since rqlite is stateless,
|
||||||
there really is no "connection". However, this type holds
|
there really is no "connection". However, this type holds
|
||||||
information such as the current leader, peers, connection
|
information such as the current leader, peers, connection
|
||||||
string to build URLs, etc.
|
string to build URLs, etc.
|
||||||
|
|
||||||
Connections are assigned a "connection ID" which is a pseudo-UUID
|
Connections are assigned a "connection ID" which is a pseudo-UUID
|
||||||
for connection identification in trace output only. This helps
|
for connection identification in trace output only. This helps
|
||||||
sort out what's going on if you have multiple connections going
|
sort out what's going on if you have multiple connections going
|
||||||
at once. It's generated using a non-standards-or-anything-else-compliant
|
at once. It's generated using a non-standards-or-anything-else-compliant
|
||||||
function that uses crypto/rand to generate 16 random bytes.
|
function that uses crypto/rand to generate 16 random bytes.
|
||||||
|
|
||||||
Note that the Connection objection holds info on all peers, gathered
|
Note that the Connection objection holds info on all peers, gathered
|
||||||
|
@ -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,8 +85,8 @@ 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))
|
||||||
}
|
}
|
||||||
|
|
||||||
/* *****************************************************************
|
/* *****************************************************************
|
||||||
|
@ -171,7 +170,7 @@ func (conn *Connection) SetConsistencyLevel(levelDesired string) error {
|
||||||
be the leader. The next thing Open() does is updateClusterInfo()
|
be the leader. The next thing Open() does is updateClusterInfo()
|
||||||
so the truth will be revealed soon enough.
|
so the truth will be revealed soon enough.
|
||||||
|
|
||||||
initConnection() does not talk to rqlite. It only parses the
|
initConnection() does not talk to rqlite. It only parses the
|
||||||
connection URL and prepares the new connection for work.
|
connection URL and prepares the new connection for work.
|
||||||
|
|
||||||
URL format:
|
URL format:
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
24
gorqlite.go
24
gorqlite.go
|
@ -10,11 +10,11 @@
|
||||||
|
|
||||||
Learn more about rqlite at: https://github.com/rqlite/rqlite
|
Learn more about rqlite at: https://github.com/rqlite/rqlite
|
||||||
|
|
||||||
*/
|
*/
|
||||||
package gorqlite
|
package gorqlite
|
||||||
|
|
||||||
/*
|
/*
|
||||||
this file contains package-level stuff:
|
this file contains package-level stuff:
|
||||||
consts
|
consts
|
||||||
init()
|
init()
|
||||||
Open, TraceOn(), TraceOff()
|
Open, TraceOn(), TraceOff()
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,7 +126,7 @@ func Open(connURL string) (Connection, error) {
|
||||||
|
|
||||||
func: trace()
|
func: trace()
|
||||||
|
|
||||||
adds a message to the trace output
|
adds a message to the trace output
|
||||||
|
|
||||||
not a public function. we (inside) can add - outside they can
|
not a public function. we (inside) can add - outside they can
|
||||||
only see.
|
only see.
|
||||||
|
@ -132,7 +134,7 @@ func Open(connURL string) (Connection, error) {
|
||||||
Call trace as: Sprintf pattern , args...
|
Call trace as: Sprintf pattern , args...
|
||||||
|
|
||||||
This is done so that the more expensive Sprintf() stuff is
|
This is done so that the more expensive Sprintf() stuff is
|
||||||
done only if truly needed. When tracing is off, calls to
|
done only if truly needed. When tracing is off, calls to
|
||||||
trace() just hit a bool check and return. If tracing is on,
|
trace() just hit a bool check and return. If tracing is on,
|
||||||
then the Sprintfing is done at a leisurely pace because, well,
|
then the Sprintfing is done at a leisurely pace because, well,
|
||||||
we're tracing.
|
we're tracing.
|
||||||
|
@ -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))
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
135
query.go
135
query.go
|
@ -91,51 +91,51 @@ 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
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Query() is used to perform SELECT operations in the database.
|
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,§ions)
|
err = json.Unmarshal(response, §ions)
|
||||||
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,13 +145,13 @@ 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
|
||||||
|
|
||||||
// r is a hash with columns, types, values, and time
|
// r is a hash with columns, types, values, and time
|
||||||
thisResult := r.(map[string]interface{})
|
thisResult := r.(map[string]interface{})
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -232,7 +232,7 @@ type QueryResult struct {
|
||||||
|
|
||||||
/* *****************************************************************
|
/* *****************************************************************
|
||||||
|
|
||||||
method: QueryResult.Columns()
|
method: QueryResult.Columns()
|
||||||
|
|
||||||
* *****************************************************************/
|
* *****************************************************************/
|
||||||
|
|
||||||
|
@ -252,7 +252,7 @@ func (qr *QueryResult) Columns() []string {
|
||||||
/*
|
/*
|
||||||
Map() returns the current row (as advanced by Next()) as a map[string]interface{}
|
Map() returns the current row (as advanced by Next()) as a map[string]interface{}
|
||||||
|
|
||||||
The key is a string corresponding to a column name.
|
The key is a string corresponding to a column name.
|
||||||
The value is the corresponding column.
|
The value is the corresponding column.
|
||||||
|
|
||||||
Note that only json values are supported, so you will need to type the interface{} accordingly.
|
Note that only json values are supported, so you will need to type the interface{} accordingly.
|
||||||
|
@ -260,13 +260,13 @@ Note that only json values are supported, so you will need to type the interface
|
||||||
func (qr *QueryResult) Map() (map[string]interface{}, error) {
|
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]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -290,15 +290,15 @@ A common idiom:
|
||||||
rows := conn.Write(something)
|
rows := conn.Write(something)
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
// your Scan/Map and processing here.
|
// your Scan/Map and processing here.
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
qr.rowNumber += 1
|
qr.rowNumber += 1
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
/* *****************************************************************
|
/* *****************************************************************
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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,51 +71,51 @@ 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()
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Logf("trying Close")
|
t.Logf("trying Close")
|
||||||
conn.Close()
|
conn.Close()
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
73
write.go
73
write.go
|
@ -1,7 +1,7 @@
|
||||||
package gorqlite
|
package gorqlite
|
||||||
|
|
||||||
/*
|
/*
|
||||||
this file has
|
this file has
|
||||||
Write()
|
Write()
|
||||||
WriteResult and its methods
|
WriteResult and its methods
|
||||||
*/
|
*/
|
||||||
|
@ -58,15 +58,15 @@ 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
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Write() is used to perform DDL/DML in the database. ALTER, CREATE, DELETE, DROP, INSERT, UPDATE, etc. all go through Write().
|
Write() is used to perform DDL/DML in the database. ALTER, CREATE, DELETE, DROP, INSERT, UPDATE, etc. all go through Write().
|
||||||
|
@ -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,§ions)
|
err = json.Unmarshal(response, §ions)
|
||||||
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,44 +115,44 @@ 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
|
||||||
thisWR.conn = conn
|
thisWR.conn = conn
|
||||||
|
|
||||||
// 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
|
||||||
}
|
}
|
||||||
|
|
||||||
_, ok = thisResult["last_insert_id"]
|
_, ok = thisResult["last_insert_id"]
|
||||||
if ok {
|
if ok {
|
||||||
thisWR.LastInsertID = int64(thisResult["last_insert_id"].(float64))
|
thisWR.LastInsertID = int64(thisResult["last_insert_id"].(float64))
|
||||||
}
|
}
|
||||||
|
|
||||||
_, ok = thisResult["rows_affected"] // could be zero for a CREATE
|
_, ok = thisResult["rows_affected"] // could be zero for a CREATE
|
||||||
if ok {
|
if ok {
|
||||||
thisWR.RowsAffected = int64(thisResult["rows_affected"].(float64))
|
thisWR.RowsAffected = int64(thisResult["rows_affected"].(float64))
|
||||||
}
|
}
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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")
|
|
||||||
s = make([]string,0)
|
|
||||||
s = append(s, "DROP TABLE IF EXISTS " + testTableName() + "")
|
|
||||||
results, err = conn.Write(s)
|
|
||||||
if ( err != nil ) {
|
|
||||||
t.Logf("--> FAILED")
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
t.Logf("trying Write DROP")
|
||||||
|
s = make([]string, 0)
|
||||||
|
s = append(s, "DROP TABLE IF EXISTS "+testTableName()+"")
|
||||||
|
results, err = conn.Write(s)
|
||||||
|
if err != nil {
|
||||||
|
t.Logf("--> FAILED")
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue