-
Notifications
You must be signed in to change notification settings - Fork 6
/
stmt.go
141 lines (120 loc) · 4.08 KB
/
stmt.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
141
// Copyright 2018-19 PJ Engineering and Business Solutions Pty. Ltd. All rights reserved.
package sql
import (
"context"
stdSql "database/sql"
"time"
)
// Stmt is a prepared statement.
// A Stmt is safe for concurrent use by multiple goroutines.
type Stmt struct {
stmt *stdSql.Stmt
killerPool StdSQLDB
connectionID string
kto time.Duration
}
// Unleak will release the reference to the killerPool
// in order to prevent a memory leak.
func (s *Stmt) Unleak() {
s.killerPool = nil
s.connectionID = ""
s.kto = 0
}
// Close closes the statement.
func (s *Stmt) Close() error {
err := s.stmt.Close()
if err != nil {
return err
}
s.Unleak() // Should this be called in a defer to guarantee it gets called?
return nil
}
// Exec executes a prepared statement with the given arguments and
// returns a Result summarizing the effect of the statement.
func (s *Stmt) Exec(args ...interface{}) (stdSql.Result, error) {
return s.ExecContext(context.Background(), args...)
}
// ExecContext executes a prepared statement with the given arguments and
// returns a Result summarizing the effect of the statement.
func (s *Stmt) ExecContext(ctx context.Context, args ...interface{}) (stdSql.Result, error) {
// Create a context that is used to cancel ExecContext()
cancelCtx, cancelFunc := context.WithCancel(context.Background())
defer cancelFunc()
outChan := make(chan stdSql.Result)
errChan := make(chan error)
returnedChan := make(chan struct{}) // Used to indicate that this function has returned
defer func() {
returnedChan <- struct{}{}
}()
go func() {
select {
case <-ctx.Done():
// context has been canceled
kill(s.killerPool, s.connectionID, s.kto)
errChan <- ctx.Err()
case <-returnedChan:
}
}()
go func() {
res, err := s.stmt.ExecContext(cancelCtx, args...)
if err != nil {
errChan <- err
return
}
outChan <- res
}()
select {
case err := <-errChan:
return nil, err
case out := <-outChan:
return out, nil
}
}
// Query executes a prepared query statement with the given arguments
// and returns the query results as a *Rows.
func (s *Stmt) Query(args ...interface{}) (*Rows, error) {
return s.QueryContext(context.Background(), args...)
}
// QueryContext executes a prepared query statement with the given arguments
// and returns the query results as a *Rows.
func (s *Stmt) QueryContext(ctx context.Context, args ...interface{}) (*Rows, error) {
// We can't use the same approach used in ExecContext because defer cancelFunc()
// cancels rows.Scan.
defer func() {
if ctx.Err() != nil {
kill(s.killerPool, s.connectionID, s.kto)
}
}()
rows, err := s.stmt.QueryContext(ctx, args...)
return &Rows{ctx: ctx, rows: rows, killerPool: s.killerPool, connectionID: s.connectionID}, err
}
// QueryRow executes a prepared query statement with the given arguments.
// If an error occurs during the execution of the statement, that error will
// be returned by a call to Scan on the returned *Row, which is always non-nil.
// If the query selects no rows, the *Row's Scan will return ErrNoRows.
// Otherwise, the *Row's Scan scans the first selected row and discards
// the rest.
//
// Example usage:
//
// var name string
// err := nameByUseridStmt.QueryRow(id).Scan(&name)
func (s *Stmt) QueryRow(args ...interface{}) *Row {
return s.QueryRowContext(context.Background(), args...)
}
// QueryRowContext executes a prepared query statement with the given arguments.
// If an error occurs during the execution of the statement, that error will
// be returned by a call to Scan on the returned *Row, which is always non-nil.
// If the query selects no rows, the *Row's Scan will return ErrNoRows.
// Otherwise, the *Row's Scan scans the first selected row and discards
// the rest.
func (s *Stmt) QueryRowContext(ctx context.Context, args ...interface{}) *Row {
// Since sql.Row does not export err field, this is the best we can do:
defer func() {
if ctx.Err() != nil {
kill(s.killerPool, s.connectionID, s.kto)
}
}()
row := s.stmt.QueryRowContext(ctx, args...)
return &Row{ctx: ctx, row: row, killerPool: s.killerPool, connectionID: s.connectionID}
}