write.go 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. package gorqlite
  2. /*
  3. this file has
  4. Write()
  5. WriteResult and its methods
  6. */
  7. import "errors"
  8. import "encoding/json"
  9. import "fmt"
  10. /* *****************************************************************
  11. method: Connection.Write()
  12. This is the JSON we get back:
  13. {
  14. "results": [
  15. {
  16. "last_insert_id": 1,
  17. "rows_affected": 1,
  18. "time": 0.00759015
  19. },
  20. {
  21. "last_insert_id": 2,
  22. "rows_affected": 1,
  23. "time": 0.00669015
  24. }
  25. ],
  26. "time": 0.869015
  27. }
  28. or
  29. {
  30. "results": [
  31. {
  32. "error": "table foo already exists"
  33. }
  34. ],
  35. "time": 0.18472685400000002
  36. }
  37. We don't care about the overall time. We just want the results,
  38. so we'll take those and put each into a WriteResult
  39. Because the results themselves are smaller than the JSON
  40. (which repeats strings like "last_insert_id" frequently),
  41. we'll just parse everything at once.
  42. * *****************************************************************/
  43. /*
  44. WriteOne() is a convenience method that wraps Write() into a single-statement
  45. method.
  46. */
  47. func (conn *Connection) WriteOne(sqlStatement string) (wr WriteResult, err error) {
  48. if conn.hasBeenClosed {
  49. wr.Err = errClosed
  50. return wr, errClosed
  51. }
  52. sqlStatements := make([]string, 0)
  53. sqlStatements = append(sqlStatements, sqlStatement)
  54. wra, err := conn.Write(sqlStatements)
  55. return wra[0], err
  56. }
  57. /*
  58. Write() is used to perform DDL/DML in the database. ALTER, CREATE, DELETE, DROP, INSERT, UPDATE, etc. all go through Write().
  59. Write() takes an array of SQL statements, and returns an equal-sized array of WriteResults, each corresponding to the SQL statement that produced it.
  60. All statements are executed as a single transaction.
  61. 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.
  62. */
  63. func (conn *Connection) Write(sqlStatements []string) (results []WriteResult, err error) {
  64. results = make([]WriteResult, 0)
  65. if conn.hasBeenClosed {
  66. var errResult WriteResult
  67. errResult.Err = errClosed
  68. results = append(results, errResult)
  69. return results, errClosed
  70. }
  71. trace("%s: Write() for %d statements", conn.ID, len(sqlStatements))
  72. response, err := conn.rqliteApiPost(api_WRITE, sqlStatements)
  73. if err != nil {
  74. trace("%s: rqliteApiCall() ERROR: %s", conn.ID, err.Error())
  75. var errResult WriteResult
  76. errResult.Err = err
  77. results = append(results, errResult)
  78. return results, err
  79. }
  80. trace("%s: rqliteApiCall() OK", conn.ID)
  81. var sections map[string]interface{}
  82. err = json.Unmarshal(response, &sections)
  83. if err != nil {
  84. trace("%s: json.Unmarshal() ERROR: %s", conn.ID, err.Error())
  85. var errResult WriteResult
  86. errResult.Err = err
  87. results = append(results, errResult)
  88. return results, err
  89. }
  90. /*
  91. at this point, we have a "results" section and
  92. a "time" section. we can igore the latter.
  93. */
  94. resultsArray := sections["results"].([]interface{})
  95. trace("%s: I have %d result(s) to parse", conn.ID, len(resultsArray))
  96. numStatementErrors := 0
  97. for n, k := range resultsArray {
  98. trace("%s: starting on result %d", conn.ID, n)
  99. thisResult := k.(map[string]interface{})
  100. var thisWR WriteResult
  101. thisWR.conn = conn
  102. // did we get an error?
  103. _, ok := thisResult["error"]
  104. if ok {
  105. trace("%s: have an error on this result: %s", conn.ID, thisResult["error"].(string))
  106. thisWR.Err = errors.New(thisResult["error"].(string))
  107. results = append(results, thisWR)
  108. numStatementErrors += 1
  109. continue
  110. }
  111. _, ok = thisResult["last_insert_id"]
  112. if ok {
  113. thisWR.LastInsertID = int64(thisResult["last_insert_id"].(float64))
  114. }
  115. _, ok = thisResult["rows_affected"] // could be zero for a CREATE
  116. if ok {
  117. thisWR.RowsAffected = int64(thisResult["rows_affected"].(float64))
  118. }
  119. thisWR.Timing = thisResult["time"].(float64)
  120. trace("%s: this result (LII,RA,T): %d %d %f", conn.ID, thisWR.LastInsertID, thisWR.RowsAffected, thisWR.Timing)
  121. results = append(results, thisWR)
  122. }
  123. trace("%s: finished parsing, returning %d results", conn.ID, len(results))
  124. if numStatementErrors > 0 {
  125. return results, errors.New(fmt.Sprintf("there were %d statement errors", numStatementErrors))
  126. } else {
  127. return results, nil
  128. }
  129. }
  130. /* *****************************************************************
  131. type: WriteResult
  132. * *****************************************************************/
  133. /*
  134. A WriteResult holds the result of a single statement sent to Write().
  135. Write() returns an array of WriteResult vars, while WriteOne() returns a single WriteResult.
  136. */
  137. type WriteResult struct {
  138. Err error // don't trust the rest if this isn't nil
  139. Timing float64
  140. RowsAffected int64 // affected by the change
  141. LastInsertID int64 // if relevant, otherwise zero value
  142. conn *Connection
  143. }