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

129
api.go
View File

@ -1,6 +1,6 @@
package gorqlite
/*
/*
this file has low level stuff:
rqliteApiGet()
@ -33,69 +33,69 @@ import "time"
func (conn *Connection) rqliteApiGet(apiOp apiOperation) ([]byte, error) {
var responseBody []byte
trace("%s: rqliteApiGet() called",conn.ID)
trace("%s: rqliteApiGet() called", conn.ID)
// 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")
}
// just to be safe, check this
peersToTry := conn.cluster.makePeerList()
if ( len(peersToTry) < 1 ) {
if len(peersToTry) < 1 {
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
// about why each failed
failureLog := make([]string,0)
failureLog := make([]string, 0)
PeerLoop:
for peerNum, peerToTry := range peersToTry {
trace("%s: attemping to contact peer %d",conn.ID,peerNum)
// docs say default GET policy is up to 10 follows automatically
url := conn.assembleURL(api_STATUS,peerToTry)
req, err := http.NewRequest("GET",url,nil)
if ( err != nil ) {
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()))
for peerNum, peerToTry := range peersToTry {
trace("%s: attemping to contact peer %d", conn.ID, peerNum)
// docs say default GET policy is up to 10 follows automatically
url := conn.assembleURL(api_STATUS, peerToTry)
req, err := http.NewRequest("GET", url, nil)
if err != nil {
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()))
continue PeerLoop
}
trace("%s: http.NewRequest() OK")
req.Header.Set("Content-Type","application/json")
trace("%s: http.NewRequest() OK")
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
client.Timeout = time.Duration(conn.timeout) * time.Second
response, err := client.Do(req)
if ( err != nil ) {
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()))
if err != nil {
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()))
continue PeerLoop
}
defer response.Body.Close()
trace("%s: client.Do() OK")
trace("%s: client.Do() OK")
responseBody, err := ioutil.ReadAll(response.Body)
if ( err != nil ) {
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()))
if err != nil {
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()))
continue PeerLoop
}
trace("%s: ioutil.ReadAll() OK")
if ( response.Status != "200 OK" ) {
trace("%s: got code %s",conn.ID,response.Status)
failureLog = append(failureLog,fmt.Sprintf("%s failed, got: %s",url,response.Status))
trace("%s: ioutil.ReadAll() OK")
if response.Status != "200 OK" {
trace("%s: got code %s", conn.ID, response.Status)
failureLog = append(failureLog, fmt.Sprintf("%s failed, got: %s", url, response.Status))
continue PeerLoop
}
// 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
}
}
// if we got here, all peers failed. Let's build a verbose error message
var stringBuffer bytes.Buffer
stringBuffer.WriteString("tried all peers unsuccessfully. here are the results:\n")
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())
}
@ -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
switch (apiOp) {
case api_QUERY:
trace("%s: rqliteApiGet() post called for a QUERY of %d statements",conn.ID,len(sqlStatements))
case api_WRITE:
trace("%s: rqliteApiGet() post called for a QUERY of %d statements",conn.ID,len(sqlStatements))
default:
return responseBody, errors.New("weird! called for an invalid apiOperation in rqliteApiPost()")
switch apiOp {
case api_QUERY:
trace("%s: rqliteApiGet() post called for a QUERY of %d statements", conn.ID, len(sqlStatements))
case api_WRITE:
trace("%s: rqliteApiGet() post called for a QUERY of %d statements", conn.ID, len(sqlStatements))
default:
return responseBody, errors.New("weird! called for an invalid apiOperation in rqliteApiPost()")
}
// 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)
if ( err != nil ) { return nil, err }
jStatements, err := json.Marshal(sqlStatements)
if err != nil {
return nil, err
}
// just to be safe, check this
peersToTry := conn.cluster.makePeerList()
if ( len(peersToTry) < 1 ) {
if len(peersToTry) < 1 {
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
// about why each failed
failureLog := make([]string,0)
failureLog := make([]string, 0)
PeerLoop:
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
// automatically followed, so we have to do that ourselves
responseStatus := "Haven't Tried Yet"
var url string
for ( responseStatus == "Haven't Tried Yet" || responseStatus == "301 Moved Permanently" ) {
url = conn.assembleURL(apiOp,peer)
req, err := http.NewRequest("POST",url,bytes.NewBuffer(jStatements))
if ( err != nil ) {
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()))
for responseStatus == "Haven't Tried Yet" || responseStatus == "301 Moved Permanently" {
url = conn.assembleURL(apiOp, peer)
req, err := http.NewRequest("POST", url, bytes.NewBuffer(jStatements))
if err != nil {
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()))
continue PeerLoop
}
req.Header.Set("Content-Type","application/json")
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
response, err := client.Do(req)
if ( err != nil ) {
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()))
if err != nil {
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()))
continue PeerLoop
}
defer response.Body.Close()
responseBody, err = ioutil.ReadAll(response.Body)
if ( err != nil ) {
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()))
if err != nil {
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()))
continue PeerLoop
}
responseStatus = response.Status
if ( responseStatus == "301 Moved Permanently" ) {
if responseStatus == "301 Moved Permanently" {
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]
continue PeerLoop
} else if ( responseStatus == "200 OK" ) {
trace("%s: api call OK, returning",conn.ID)
} else if responseStatus == "200 OK" {
trace("%s: api call OK, returning", conn.ID)
return responseBody, nil
} else {
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
}
}
@ -195,8 +197,7 @@ PeerLoop:
var stringBuffer bytes.Buffer
stringBuffer.WriteString("tried all peers unsuccessfully. here are the results:\n")
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())
}

View File

@ -5,7 +5,7 @@ package gorqlite
types:
peer
rqliteCluster
rqliteCluster
Connection methods:
assembleURL (from a peer)
updateClusterInfo (does the full cluster discovery via status)
@ -32,19 +32,18 @@ import "strings"
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.
* *****************************************************************/
type peer struct {
hostname string // hostname or "localhost"
port string // "4001" or port, only ever used as a string
hostname string // hostname or "localhost"
port string // "4001" or port, only ever used as a 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 {
leader peer
leader peer
otherPeers []peer
conn *Connection
conn *Connection
}
/* *****************************************************************
@ -72,16 +71,16 @@ type rqliteCluster struct {
* *****************************************************************/
func (rc *rqliteCluster) makePeerList() []peer {
trace("%s: makePeerList() called",rc.conn.ID)
trace("%s: makePeerList() called", rc.conn.ID)
var peerList []peer
peerList = append(peerList,rc.leader)
peerList = append(peerList, rc.leader)
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 {
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
@ -105,13 +104,13 @@ func (rc *rqliteCluster) makePeerList() []peer {
func (conn *Connection) assembleURL(apiOp apiOperation, p peer) string {
var stringBuffer bytes.Buffer
if ( conn.wantsHTTPS == true ) {
if conn.wantsHTTPS == true {
stringBuffer.WriteString("https")
} else {
stringBuffer.WriteString("http")
}
stringBuffer.WriteString("://")
if ( conn.username != "" && conn.password != "" ) {
if conn.username != "" && conn.password != "" {
stringBuffer.WriteString(conn.username)
stringBuffer.WriteString(":")
stringBuffer.WriteString(conn.password)
@ -122,26 +121,26 @@ func (conn *Connection) assembleURL(apiOp apiOperation, p peer) string {
stringBuffer.WriteString(p.port)
switch apiOp {
case api_STATUS:
stringBuffer.WriteString("/status")
case api_QUERY:
stringBuffer.WriteString("/db/query")
case api_WRITE:
stringBuffer.WriteString("/db/execute")
case api_STATUS:
stringBuffer.WriteString("/status")
case api_QUERY:
stringBuffer.WriteString("/db/query")
case api_WRITE:
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(consistencyLevelNames[conn.consistencyLevel])
}
switch apiOp {
case api_QUERY:
trace("%s: assembled URL for an api_QUERY: %s",conn.ID,stringBuffer.String())
case api_STATUS:
trace("%s: assembled URL for an api_STATUS: %s",conn.ID,stringBuffer.String())
case api_WRITE:
trace("%s: assembled URL for an api_WRITE: %s",conn.ID,stringBuffer.String())
case api_QUERY:
trace("%s: assembled URL for an api_QUERY: %s", conn.ID, stringBuffer.String())
case api_STATUS:
trace("%s: assembled URL for an api_STATUS: %s", conn.ID, stringBuffer.String())
case api_WRITE:
trace("%s: assembled URL for an api_WRITE: %s", conn.ID, stringBuffer.String())
}
return stringBuffer.String()
@ -160,22 +159,26 @@ func (conn *Connection) assembleURL(apiOp apiOperation, p peer) string {
* *****************************************************************/
func (conn *Connection) updateClusterInfo() error {
trace("%s: updateClusterInfo() called",conn.ID)
trace("%s: updateClusterInfo() called", conn.ID)
// start with a fresh new cluster
var rc rqliteCluster
rc.conn = conn
responseBody, err := conn.rqliteApiGet(api_STATUS)
if ( err != nil ) { return err }
trace("%s: updateClusterInfo() back from api call OK",conn.ID)
if err != nil {
return err
}
trace("%s: updateClusterInfo() back from api call OK", conn.ID)
sections := make(map[string]interface{})
err = json.Unmarshal(responseBody,&sections)
if ( err != nil ) { return err }
err = json.Unmarshal(responseBody, &sections)
if err != nil {
return err
}
sMap := sections["store"].(map[string]interface{})
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
// 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{})
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 */
var p peer
parts := strings.Split(httpAddr.(string),":")
parts := strings.Split(httpAddr.(string), ":")
p.hostname = parts[0]
p.port = parts[1]
// so is this the leader?
if ( leaderRaftAddr == raftAddr ) {
trace ("%s: found leader at %s",conn.ID,httpAddr)
if leaderRaftAddr == raftAddr {
trace("%s: found leader at %s", conn.ID, httpAddr)
rc.leader = p
} else {
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")
}
// dump to trace
trace("%s: here is my cluster config:",conn.ID)
trace("%s: leader : %s",conn.ID,rc.leader.String())
trace("%s: here is my cluster config:", conn.ID)
trace("%s: leader : %s", conn.ID, rc.leader.String())
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
conn.cluster = rc
return nil
}

View File

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

119
conn.go
View File

@ -35,13 +35,13 @@ var wantsTrace bool
/*
The connection abstraction. Note that since rqlite is stateless,
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.
Connections are assigned a "connection ID" which is a pseudo-UUID
for connection identification in trace output only. This helps
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.
Note that the Connection objection holds info on all peers, gathered
@ -49,23 +49,22 @@ var wantsTrace bool
*/
type Connection struct {
cluster rqliteCluster
/*
name type default
*/
name type default
*/
username string // username or ""
password string // username or ""
consistencyLevel consistencyLevel // WEAK
wantsHTTPS bool // false unless connection URL is https
username string // username or ""
password string // username or ""
consistencyLevel consistencyLevel // WEAK
wantsHTTPS bool // false unless connection URL is https
// variables below this line need to be initialized in Open()
timeout int // 10
hasBeenClosed bool // false
ID string // generated in init()
timeout int // 10
hasBeenClosed bool // false
ID string // generated in init()
}
/* *****************************************************************
@ -76,7 +75,7 @@ type Connection struct {
func (conn *Connection) Close() {
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) {
if ( conn.hasBeenClosed) {
return "", errClosed
if conn.hasBeenClosed {
return "", errClosed
}
return consistencyLevelNames[conn.consistencyLevel], nil
}
@ -99,16 +98,16 @@ func (conn *Connection) ConsistencyLevel() (string, error) {
* *****************************************************************/
func (conn *Connection) Leader() (string, error) {
if ( conn.hasBeenClosed) {
return "", errClosed
if conn.hasBeenClosed {
return "", errClosed
}
trace("%s: Leader(), calling updateClusterInfo()",conn.ID)
trace("%s: Leader(), calling updateClusterInfo()", conn.ID)
err := conn.updateClusterInfo()
if ( err != nil ) {
trace("%s: Leader() got error from updateClusterInfo(): %s",conn.ID,err.Error())
if err != nil {
trace("%s: Leader() got error from updateClusterInfo(): %s", conn.ID, err.Error())
return "", err
} else {
trace("%s: Leader(), updateClusterInfo() OK",conn.ID)
trace("%s: Leader(), updateClusterInfo() OK", conn.ID)
}
return conn.cluster.leader.String(), nil
}
@ -120,23 +119,23 @@ func (conn *Connection) Leader() (string, error) {
* *****************************************************************/
func (conn *Connection) Peers() ([]string, error) {
if ( conn.hasBeenClosed) {
if conn.hasBeenClosed {
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()
if ( err != nil ) {
trace("%s: Peers() got error from updateClusterInfo(): %s",conn.ID,err.Error())
if err != nil {
trace("%s: Peers() got error from updateClusterInfo(): %s", conn.ID, err.Error())
return plist, err
} 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 {
plist = append(plist,p.String())
plist = append(plist, p.String())
}
return plist, nil
}
@ -148,15 +147,15 @@ func (conn *Connection) Peers() ([]string, error) {
* *****************************************************************/
func (conn *Connection) SetConsistencyLevel(levelDesired string) error {
if ( conn.hasBeenClosed) {
return errClosed
if conn.hasBeenClosed {
return errClosed
}
_, ok := consistencyLevels[levelDesired]
if ( ok ) {
if ok {
conn.consistencyLevel = consistencyLevels[levelDesired]
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()
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.
URL format:
@ -204,21 +203,21 @@ func (conn *Connection) initConnection(url string) error {
// do some sanity checks. You know users.
if ( len(url) < 7 ) {
if len(url) < 7 {
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'")
}
u, err := nurl.Parse(url)
if ( err != nil ) {
if err != nil {
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
}
@ -233,27 +232,27 @@ func (conn *Connection) initConnection(url string) error {
// not guaranteed, so test if set
pass, isset := u.User.Password()
if ( isset ) {
if isset {
conn.password = pass
} else {
conn.password = ""
}
}
if ( u.Host == "" ) {
if u.Host == "" {
conn.cluster.leader.hostname = "localhost"
} else {
conn.cluster.leader.hostname = u.Host
}
if ( u.Host == "" ) {
if u.Host == "" {
conn.cluster.leader.hostname = "localhost"
conn.cluster.leader.port = "4001"
} else {
// SplitHostPort() should only return an error if there is no host port.
// I think.
h, p, err := net.SplitHostPort(u.Host)
if ( err != nil ) {
if err != nil {
conn.cluster.leader.hostname = u.Host
} else {
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
the desired consistency level
at the moment, the only allowed query is "level=" with
the desired consistency level
*/
// default
conn.consistencyLevel = cl_WEAK
if ( u.RawQuery != "" ) {
if ( u.RawQuery == "level=weak") {
if u.RawQuery != "" {
if u.RawQuery == "level=weak" {
// that's ok but nothing to do
} else if ( u.RawQuery == "level=strong" ) {
} else if u.RawQuery == "level=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
} else {
return errors.New("don't know what to do with this query: " + u.RawQuery)
}
}
trace("%s: parseDefaultPeer() is done:",conn.ID)
if ( conn.wantsHTTPS == true ) {
trace("%s: %s -> %s",conn.ID,"wants https?","yes")
trace("%s: parseDefaultPeer() is done:", conn.ID)
if conn.wantsHTTPS == true {
trace("%s: %s -> %s", conn.ID, "wants https?", "yes")
} 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,"password",conn.password)
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,"consistencyLevel",consistencyLevelNames[conn.consistencyLevel])
trace("%s: %s -> %s", conn.ID, "username", conn.username)
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, "port", conn.cluster.leader.port)
trace("%s: %s -> %s", conn.ID, "consistencyLevel", consistencyLevelNames[conn.consistencyLevel])
conn.cluster.conn = conn
return nil
}

View File

@ -10,11 +10,11 @@
Learn more about rqlite at: https://github.com/rqlite/rqlite
*/
*/
package gorqlite
/*
this file contains package-level stuff:
this file contains package-level stuff:
consts
init()
Open, TraceOn(), TraceOff()
@ -33,16 +33,19 @@ import "strings"
* *****************************************************************/
type consistencyLevel int
const (
cl_NONE consistencyLevel = iota
cl_WEAK
cl_STRONG
)
// used in several places, actually
var consistencyLevelNames map[consistencyLevel]string
var consistencyLevels map[string]consistencyLevel
type apiOperation int
const (
api_QUERY apiOperation = iota
api_STATUS
@ -56,7 +59,7 @@ const (
* *****************************************************************/
func init() {
traceOut = ioutil.Discard
traceOut = ioutil.Discard
consistencyLevelNames = make(map[consistencyLevel]string)
consistencyLevelNames[cl_NONE] = "none"
@ -70,7 +73,6 @@ func init() {
}
/* *****************************************************************
Open() creates and returns a "connection" to rqlite.
@ -99,7 +101,7 @@ func Open(connURL string) (Connection, error) {
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:])
trace("%s: Open() called for url: %s",conn.ID,connURL)
trace("%s: Open() called for url: %s", conn.ID, connURL)
// set defaults
conn.timeout = 10
@ -107,7 +109,7 @@ func Open(connURL string) (Connection, error) {
// parse the URL given
err = conn.initConnection(connURL)
if ( err != nil ) {
if err != nil {
return conn, err
}
@ -124,7 +126,7 @@ func Open(connURL string) (Connection, error) {
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
only see.
@ -132,7 +134,7 @@ func Open(connURL string) (Connection, error) {
Call trace as: Sprintf pattern , args...
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,
then the Sprintfing is done at a leisurely pace because, well,
we're tracing.
@ -146,7 +148,7 @@ func Open(connURL string) (Connection, error) {
func trace(pattern string, args ...interface{}) {
// don't do the probably expensive Sprintf() if not needed
if ( wantsTrace == false ) {
if wantsTrace == false {
return
}
@ -156,8 +158,8 @@ func trace(pattern string, args ...interface{}) {
// make sure there is one and only one newline
nlPattern := strings.TrimSpace(pattern) + "\n"
msg := fmt.Sprintf(nlPattern,args...)
traceOut.Write( []byte( msg ) )
msg := fmt.Sprintf(nlPattern, args...)
traceOut.Write([]byte(msg))
}
/*

135
query.go
View File

@ -91,51 +91,51 @@ QueryOne() is a convenience method that wraps Query() into a single-statement
method.
*/
func (conn *Connection) QueryOne(sqlStatement string) (qr QueryResult, err error) {
if ( conn.hasBeenClosed) {
if conn.hasBeenClosed {
qr.Err = errClosed
return qr, errClosed
}
sqlStatements := make([]string,0)
sqlStatements = append(sqlStatements,sqlStatement)
}
sqlStatements := make([]string, 0)
sqlStatements = append(sqlStatements, sqlStatement)
qra, err := conn.Query(sqlStatements)
return qra[0], err
}
}
/*
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.
*/
*/
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
errResult.Err = errClosed
results = append(results,errResult)
return results, errClosed
}
trace("%s: Query() for %d statements",conn.ID,len(sqlStatements))
results = append(results, errResult)
return results, errClosed
}
trace("%s: Query() for %d statements", conn.ID, len(sqlStatements))
// if we get an error POSTing, that's a showstopper
response, err := conn.rqliteApiPost(api_QUERY,sqlStatements)
if ( err != nil ) {
trace("%s: rqliteApiCall() ERROR: %s",conn.ID,err.Error())
response, err := conn.rqliteApiPost(api_QUERY, sqlStatements)
if err != nil {
trace("%s: rqliteApiCall() ERROR: %s", conn.ID, err.Error())
var errResult QueryResult
errResult.Err = err
results = append(results,errResult)
results = append(results, errResult)
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
var sections map[string]interface{}
err = json.Unmarshal(response,&sections)
if ( err != nil ) {
trace("%s: json.Unmarshal() ERROR: %s",conn.ID,err.Error())
err = json.Unmarshal(response, &sections)
if err != nil {
trace("%s: json.Unmarshal() ERROR: %s", conn.ID, err.Error())
var errResult QueryResult
errResult.Err = err
results = append(results,errResult)
results = append(results, errResult)
return results, err
}
@ -145,13 +145,13 @@ func (conn *Connection) Query(sqlStatements []string) (results []QueryResult, er
*/
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
for n, r := range resultsArray {
trace("%s: parsing result %d",conn.ID,n)
trace("%s: parsing result %d", conn.ID, n)
var thisQR QueryResult
thisQR.conn = conn
thisQR.conn = conn
// r is a hash with columns, types, values, and time
thisResult := r.(map[string]interface{})
@ -159,9 +159,9 @@ func (conn *Connection) Query(sqlStatements []string) (results []QueryResult, er
// did we get an error?
_, ok := thisResult["error"]
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))
results = append(results,thisQR)
results = append(results, thisQR)
numStatementErrors++
continue
}
@ -172,28 +172,28 @@ func (conn *Connection) Query(sqlStatements []string) (results []QueryResult, er
// column & type are an array of strings
c := thisResult["columns"].([]interface{})
t := thisResult["types"].([]interface{})
for i := 0; i < len(c) ; i++ {
thisQR.columns = append(thisQR.columns,c[i].(string))
thisQR.types = append(thisQR.types,t[i].(string))
for i := 0; i < len(c); i++ {
thisQR.columns = append(thisQR.columns, c[i].(string))
thisQR.types = append(thisQR.types, t[i].(string))
}
// and values are an array of arrays
if ( thisResult["values"] != nil ) {
if thisResult["values"] != nil {
thisQR.values = thisResult["values"].([]interface{})
} else {
trace("%s: fyi, no values this query",conn.ID)
trace("%s: fyi, no values this query", conn.ID)
}
thisQR.rowNumber = -1
trace("%s: this result (#col,time) %d %f",conn.ID,len(thisQR.columns),thisQR.Timing)
results = append(results,thisQR)
trace("%s: this result (#col,time) %d %f", conn.ID, len(thisQR.columns), thisQR.Timing)
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 ) {
return results, errors.New(fmt.Sprintf("there were %d statement errors",numStatementErrors))
if numStatementErrors > 0 {
return results, errors.New(fmt.Sprintf("there were %d statement errors", numStatementErrors))
} else {
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.
*/
type QueryResult struct {
conn *Connection
Err error
columns []string
types []string
Timing float64
values []interface{}
conn *Connection
Err error
columns []string
types []string
Timing float64
values []interface{}
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{}
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.
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) {
trace("%s: Map() called for row %d", qr.conn.ID, qr.rowNumber)
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")
}
thisRowValues := qr.values[qr.rowNumber].([]interface {})
for i := 0; i<len(qr.columns); i++ {
thisRowValues := qr.values[qr.rowNumber].([]interface{})
for i := 0; i < len(qr.columns); i++ {
ans[qr.columns[i]] = thisRowValues[i]
}
@ -290,15 +290,15 @@ A common idiom:
rows := conn.Write(something)
for rows.Next() {
// your Scan/Map and processing here.
}
}
*/
func (qr *QueryResult) Next() bool {
if ( qr.rowNumber >= int64(len(qr.values) - 1 )) {
if qr.rowNumber >= int64(len(qr.values)-1) {
return false
}
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,
since sqlite does not support them.
*/
func (qr *QueryResult) Scan(dest... interface{}) error {
trace("%s: Scan() called for %d vars", qr.conn.ID,len(dest))
func (qr *QueryResult) Scan(dest ...interface{}) error {
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")
}
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)))
}
thisRowValues := qr.values[qr.rowNumber].([]interface {})
thisRowValues := qr.values[qr.rowNumber].([]interface{})
for n, d := range dest {
switch d.(type) {
case *int64:
f := int64(thisRowValues[n].(float64))
*d.(*int64) = f
case *float64:
f := float64(thisRowValues[n].(float64))
*d.(*float64) = f
case *string:
s := string(thisRowValues[n].(string))
*d.(*string) = s
default:
return errors.New(fmt.Sprintf("unknown destination type to scan into in variable #%d",n))
case *int64:
f := int64(thisRowValues[n].(float64))
*d.(*int64) = f
case *float64:
f := float64(thisRowValues[n].(float64))
*d.(*float64) = f
case *string:
s := string(thisRowValues[n].(string))
*d.(*string) = s
default:
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 {
return qr.types
}

View File

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

View File

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

View File

@ -1,7 +1,7 @@
package gorqlite
/*
this file has
this file has
Write()
WriteResult and its methods
*/
@ -58,15 +58,15 @@ method.
*/
func (conn *Connection) WriteOne(sqlStatement string) (wr WriteResult, err error) {
if ( conn.hasBeenClosed) {
if conn.hasBeenClosed {
wr.Err = errClosed
return wr, errClosed
}
sqlStatements := make([]string,0)
sqlStatements = append(sqlStatements,sqlStatement)
wra , err := conn.Write(sqlStatements)
}
sqlStatements := make([]string, 0)
sqlStatements = append(sqlStatements, sqlStatement)
wra, err := conn.Write(sqlStatements)
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().
@ -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.
*/
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
errResult.Err = errClosed
results = append(results,errResult)
results = append(results, errResult)
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)
if ( err != nil ) {
trace("%s: rqliteApiCall() ERROR: %s",conn.ID,err.Error())
response, err := conn.rqliteApiPost(api_WRITE, sqlStatements)
if err != nil {
trace("%s: rqliteApiCall() ERROR: %s", conn.ID, err.Error())
var errResult WriteResult
errResult.Err = err
results = append(results,errResult)
results = append(results, errResult)
return results, err
}
trace("%s: rqliteApiCall() OK",conn.ID)
trace("%s: rqliteApiCall() OK", conn.ID)
var sections map[string]interface{}
err = json.Unmarshal(response,&sections)
if ( err != nil ) {
trace("%s: json.Unmarshal() ERROR: %s",conn.ID,err.Error())
err = json.Unmarshal(response, &sections)
if err != nil {
trace("%s: json.Unmarshal() ERROR: %s", conn.ID, err.Error())
var errResult WriteResult
errResult.Err = err
results = append(results,errResult)
results = append(results, errResult)
return results, err
}
@ -115,44 +115,44 @@ func (conn *Connection) Write(sqlStatements []string) (results []WriteResult, er
*/
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
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{})
var thisWR WriteResult
thisWR.conn = conn
thisWR.conn = conn
// did we get an error?
_, ok := thisResult["error"]
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))
results = append(results,thisWR)
results = append(results, thisWR)
numStatementErrors += 1
continue
}
_, ok = thisResult["last_insert_id"]
if ok {
if ok {
thisWR.LastInsertID = int64(thisResult["last_insert_id"].(float64))
}
_, ok = thisResult["rows_affected"] // could be zero for a CREATE
if ok {
if ok {
thisWR.RowsAffected = int64(thisResult["rows_affected"].(float64))
}
thisWR.Timing = thisResult["time"].(float64)
trace("%s: this result (LII,RA,T): %d %d %f",conn.ID,thisWR.LastInsertID,thisWR.RowsAffected,thisWR.Timing)
results = append(results,thisWR)
trace("%s: this result (LII,RA,T): %d %d %f", conn.ID, thisWR.LastInsertID, thisWR.RowsAffected, thisWR.Timing)
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 ) {
return results, errors.New(fmt.Sprintf("there were %d statement errors",numStatementErrors))
if numStatementErrors > 0 {
return results, errors.New(fmt.Sprintf("there were %d statement errors", numStatementErrors))
} else {
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.
*/
type WriteResult struct {
Err error // don't trust the rest if this isn't nil
Timing float64
Err error // don't trust the rest if this isn't nil
Timing float64
RowsAffected int64 // affected by the change
LastInsertID int64 // if relevant, otherwise zero value
conn *Connection
conn *Connection
}

View File

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