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, §ions) 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 on this result: %s", conn.ID, thisResult["error"].(string)) thisWR.Err = errors.New(thisResult["error"].(string)) 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 }