/
transaction.go
140 lines (116 loc) · 3.12 KB
/
transaction.go
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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
package rebecca
// This file contains thin exported functions and methods related to
// Transaction only.
//
// For unexported functions see: helpers.go
//
// For Context see: context.go
import (
"errors"
"fmt"
"github.com/waterlink/rebecca/driver"
)
// Transaction is for managing transactions for drivers that allow it
type Transaction struct {
tx interface{}
finished bool
}
// Transact is for abstracting transaction handling. It commits transaction if
// fn returned nil, otherwise it rolls transaction back.
func Transact(fn func(tx *Transaction) error) (err error) {
tx, err := Begin()
if err != nil {
return
}
defer func() {
if p := recover(); p != nil {
if e, ok := p.(error); ok {
err = &Recovered{e}
} else {
err = &Recovered{fmt.Errorf("%s", p)}
}
}
if err != nil {
tx.Rollback()
return
}
err = tx.Commit()
}()
return fn(tx)
}
// Begin is for creating proper transaction
func Begin() (*Transaction, error) {
d, lock := driver.Get()
defer lock.Unlock()
tx, err := d.Begin()
if err != nil {
return nil, fmt.Errorf("Unable to begin transaction - %s", err)
}
return &Transaction{
tx: tx,
}, nil
}
// Rollback is for rolling back the transaction
func (tx *Transaction) Rollback() {
if tx.finished {
return
}
d, lock := driver.Get()
defer lock.Unlock()
d.Rollback(tx.tx)
tx.finished = true
}
// Commit is for committing the transaction
func (tx *Transaction) Commit() error {
if tx.finished {
return errors.New("Unable to commit transaction - Current transaction is already finished")
}
d, lock := driver.Get()
defer lock.Unlock()
if err := d.Commit(tx.tx); err != nil {
return fmt.Errorf("Unable to commit transaction - %s", err)
}
tx.finished = true
return nil
}
// Get is for fetching one record
func (tx *Transaction) Get(record interface{}, ID interface{}) error {
return get(tx.tx, record, ID)
}
// Save is for saving one record (either creating or updating)
func (tx *Transaction) Save(record interface{}) error {
return save(tx.tx, record)
}
// All is for fetching all records
func (tx *Transaction) All(records interface{}) error {
ctx := tx.Context(&Context{})
return ctx.All(records)
}
// Where is for fetching specific records
func (tx *Transaction) Where(records interface{}, where string, args ...interface{}) error {
ctx := tx.Context(&Context{})
return ctx.Where(records, where, args...)
}
// First is for fetching only one specific record
func (tx *Transaction) First(record interface{}, where string, args ...interface{}) error {
ctx := tx.Context(&Context{})
return ctx.First(record, where, args...)
}
// Remove is for removing the record
func (tx *Transaction) Remove(record interface{}) error {
return remove(tx.tx, record)
}
// Exec is for executing a query within transaction and discarding its result
func (tx *Transaction) Exec(query string, args ...interface{}) error {
return exec(tx.tx, query, args...)
}
// Context is for instantiating proper context for transaction
func (tx *Transaction) Context(ctx *Context) *Context {
return &Context{
Order: ctx.Order,
Group: ctx.Group,
Limit: ctx.Limit,
Skip: ctx.Skip,
tx: tx.tx,
}
}