gorqlite/write.go

180 lines
4.9 KiB
Go

package gorqlite
/*
this file has
Write()
WriteResult and its methods
*/
import "errors"
import "encoding/json"
import "fmt"
/* *****************************************************************
method: Connection.Write()
This is the JSON we get back:
{
"results": [
{
"last_insert_id": 1,
"rows_affected": 1,
"time": 0.00759015
},
{
"last_insert_id": 2,
"rows_affected": 1,
"time": 0.00669015
}
],
"time": 0.869015
}
or
{
"results": [
{
"error": "table foo already exists"
}
],
"time": 0.18472685400000002
}
We don't care about the overall time. We just want the results,
so we'll take those and put each into a WriteResult
Because the results themselves are smaller than the JSON
(which repeats strings like "last_insert_id" frequently),
we'll just parse everything at once.
* *****************************************************************/
/*
WriteOne() is a convenience method that wraps Write() into a single-statement
method.
*/
func (conn *Connection) WriteOne(sqlStatement string) (wr WriteResult, err error) {
if ( conn.hasBeenClosed) {
wr.Err = errClosed
return wr, errClosed
}
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().
Write() takes an array of SQL statements, and returns an equal-sized array of WriteResults, each corresponding to the SQL statement that produced it.
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)
if ( conn.hasBeenClosed) {
var errResult WriteResult
errResult.Err = errClosed
results = append(results,errResult)
return results, errClosed
}
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())
var errResult WriteResult
errResult.Err = err
results = append(results,errResult)
return results, err
}
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())
var errResult WriteResult
errResult.Err = err
results = append(results,errResult)
return results, err
}
/*
at this point, we have a "results" section and
a "time" section. we can igore the latter.
*/
resultsArray := sections["results"].([]interface{})
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)
thisResult := k.(map[string]interface{})
var thisWR WriteResult
thisWR.conn = conn
// did we get an error?
_, ok := thisResult["error"]
if ok {
trace("%s: have an error this this result: %s",conn.ID,thisResult["error"].(string))
thisWR.Err = err
results = append(results,thisWR)
numStatementErrors += 1
continue
}
_, ok = thisResult["last_insert_id"]
if ok {
thisWR.LastInsertID = int64(thisResult["last_insert_id"].(float64))
}
_, ok = thisResult["rows_affected"] // could be zero for a CREATE
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: finished parsing, returning %d results",conn.ID,len(results))
if ( numStatementErrors > 0 ) {
return results, errors.New(fmt.Sprintf("there were %d statement errors",numStatementErrors))
} else {
return results, nil
}
}
/* *****************************************************************
type: WriteResult
* *****************************************************************/
/*
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
RowsAffected int64 // affected by the change
LastInsertID int64 // if relevant, otherwise zero value
conn *Connection
}