forked from stephenafamo/bob
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathstdlib.go
More file actions
172 lines (139 loc) · 5.08 KB
/
Copy pathstdlib.go
File metadata and controls
172 lines (139 loc) · 5.08 KB
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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
package bob
import (
"context"
"database/sql"
"database/sql/driver"
"errors"
"fmt"
"github.com/stephenafamo/scan"
)
// Open works just like [sql.Open], but converts the returned [*sql.DB] to [DB]
func Open(driverName string, dataSource string) (DB, error) {
db, err := sql.Open(driverName, dataSource)
return NewDB(db), err
}
// OpenDB works just like [sql.OpenDB], but converts the returned [*sql.DB] to [DB]
func OpenDB(c driver.Connector) DB {
return NewDB(sql.OpenDB(c))
}
// NewDB wraps an [*sql.DB] and returns a type that implements [Queryer] but still
// retains the expected methods used by *sql.DB
// This is useful when an existing *sql.DB is used in other places in the codebase
func NewDB(db *sql.DB) DB {
return DB{db}
}
// DB is similar to *sql.DB but implement [Queryer]
type DB struct {
*sql.DB
}
// PrepareContext creates a prepared statement for later queries or executions
func (d DB) PrepareContext(ctx context.Context, query string) (StdPrepared, error) {
s, err := d.DB.PrepareContext(ctx, query)
return StdPrepared{s}, err
}
// QueryContext executes a query that returns rows, typically a SELECT. The args are for any placeholder parameters in the query.
func (d DB) QueryContext(ctx context.Context, query string, args ...any) (scan.Rows, error) {
return d.DB.QueryContext(ctx, query, args...)
}
// Begin is similar to [*sql.DB.BeginTx], but return a transaction that
// implements [Queryer]
func (d DB) Begin(ctx context.Context) (Transaction, error) {
return d.BeginTx(ctx, nil)
}
// BeginTx is similar to [*sql.DB.BeginTx], but return a transaction that
// implements [Queryer]
func (d DB) BeginTx(ctx context.Context, opts *sql.TxOptions) (Transaction, error) {
tx, err := d.DB.BeginTx(ctx, opts)
if err != nil {
return Tx{}, err
}
return NewTx(tx), nil
}
// RunInTx runs the provided function in a transaction.
// If the function returns an error, the transaction is rolled back.
// Otherwise, the transaction is committed.
func (d DB) RunInTx(ctx context.Context, txOptions *sql.TxOptions, fn func(context.Context, Executor) error) error {
tx, err := d.BeginTx(ctx, txOptions)
if err != nil {
return fmt.Errorf("begin: %w", err)
}
if err := fn(ctx, tx); err != nil {
err = fmt.Errorf("call: %w", err)
if rollbackErr := tx.Rollback(ctx); rollbackErr != nil {
return errors.Join(err, rollbackErr)
}
return err
}
if err := tx.Commit(ctx); err != nil {
return fmt.Errorf("commit: %w", err)
}
return nil
}
// NewTx wraps an [*sql.Tx] and returns a type that implements [Queryer] but still
// retains the expected methods used by *sql.Tx
// This is useful when an existing *sql.Tx is used in other places in the codebase
func NewTx(tx *sql.Tx) Tx {
return Tx{tx}
}
// Tx is similar to *sql.Tx but implements [Queryer]
type Tx struct {
*sql.Tx
}
// PrepareContext creates a prepared statement for later queries or executions
func (t Tx) PrepareContext(ctx context.Context, query string) (StdPrepared, error) {
s, err := t.Tx.PrepareContext(ctx, query)
return StdPrepared{s}, err
}
// QueryContext executes a query that returns rows, typically a SELECT. The args are for any placeholder parameters in the query.
func (t Tx) QueryContext(ctx context.Context, query string, args ...any) (scan.Rows, error) {
return t.Tx.QueryContext(ctx, query, args...)
}
// Commit works the same as [*sql.Tx.Commit]
func (t Tx) Commit(_ context.Context) error {
return t.Tx.Commit()
}
// Rollback works the same as [*sql.Tx.Rollback]
func (t Tx) Rollback(_ context.Context) error {
return t.Tx.Rollback()
}
func (tx Tx) StmtContext(ctx context.Context, stmt StdPrepared) StdPrepared {
return StdPrepared{tx.Tx.StmtContext(ctx, stmt.Stmt)}
}
// NewConn wraps an [*sql.Conn] and returns a type that implements [Queryer]
// This is useful when an existing *sql.Conn is used in other places in the codebase
func NewConn(conn *sql.Conn) Conn {
return Conn{conn}
}
// Conn is similar to *sql.Conn but implements [Queryer]
type Conn struct {
*sql.Conn
}
// PrepareContext creates a prepared statement for later queries or executions
func (d Conn) PrepareContext(ctx context.Context, query string) (StdPrepared, error) {
s, err := d.Conn.PrepareContext(ctx, query)
return StdPrepared{s}, err
}
// QueryContext executes a query that returns rows, typically a SELECT. The args are for any placeholder parameters in the query.
func (d Conn) QueryContext(ctx context.Context, query string, args ...any) (scan.Rows, error) {
return d.Conn.QueryContext(ctx, query, args...)
}
// Begin is similar to [*sql.DB.BeginTx], but return a transaction that
// implements [Queryer]
func (d Conn) Begin(ctx context.Context) (Transaction, error) {
return d.BeginTx(ctx, nil)
}
// BeginTx is similar to [*sql.DB.BeginTx], but return a transaction that
// implements [Queryer]
func (d Conn) BeginTx(ctx context.Context, opts *sql.TxOptions) (Transaction, error) {
tx, err := d.Conn.BeginTx(ctx, opts)
if err != nil {
return Tx{}, err
}
return NewTx(tx), nil
}
type StdPrepared struct {
*sql.Stmt
}
func (s StdPrepared) QueryContext(ctx context.Context, args ...any) (scan.Rows, error) {
return s.Stmt.QueryContext(ctx, args...)
}