aboutsummaryrefslogtreecommitdiff
path: root/dbstore/tx.go
blob: 460bbe1b7a0488eada54acab564c0c314aafe1b6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
// database storage
package dbstore

import (
  "context"
  db_sql "database/sql"
  "fmt"
  _ "github.com/mattn/go-sqlite3"
)

type Tx struct {
  tx      *db_sql.Tx // underlying transaction
  sts     map[string]*db_sql.Stmt // prepared statements
  closed  bool // are the statements closed?
  done    bool // is the transaction done?
  err     error // last error
}

func newTx(ctx context.Context, db *db_sql.DB, queryIds []string) (Tx, error) {
  var r Tx

  // begin context
  if tx, err := db.BeginTx(ctx, nil); err != nil {
    return r, err
  } else {
    r.tx = tx
  }

  // build query map
  queries, err := getQueries(queryIds)
  if err != nil {
    return r, err
  }

  // build statements
  sts := make(map[string]*db_sql.Stmt)
  for id, sql := range(queries) {
    if st, err := r.tx.PrepareContext(ctx, sql); err != nil {
      return r, err
    } else {
      sts[id] = st
    }
  }
  r.sts = sts

  // return success
  return r, nil
}

// Finalize statements.
func (tx Tx) Close() {
  if !tx.closed {
    for id, st := range(tx.sts) {
      // close statement
      st.Close()

      // delete key
      delete(tx.sts, id)
    }

    // mark transaction as closed
    tx.closed = true
  }
}

// Finalize statements, commit transaction.
func (tx Tx) Commit() error {
  // close statements
  // FIXME: this isn't really necessary, rollback and commit will take
  // care of it according to the database/sql docs
  tx.Close()

  if !tx.done {
    tx.err = tx.tx.Commit()
    tx.done = true
  }

  // return last error
  return tx.err
}

// Finalize statements, rollback transaction.
func (tx Tx) Rollback() error {
  // close statements
  // FIXME: this isn't really necessary, rollback and commit will take
  // care of it according to the database/sql docs
  tx.Close()

  if !tx.done {
    tx.err = tx.tx.Rollback()
    tx.done = true
  }

  // return last error
  return tx.err
}

// execute given prepared statement
func (tx Tx) Exec(ctx context.Context, id string, args... interface{}) (db_sql.Result, error) {
  if st, ok := tx.sts[id]; !ok {
    return nil, fmt.Errorf("unknown statement: %s", id)
  } else {
    return st.ExecContext(ctx, args...)
  }
}