-
Notifications
You must be signed in to change notification settings - Fork 36
/
Copy pathstmt.go
204 lines (172 loc) · 5.11 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
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
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
package gomocket
import (
"context"
"database/sql/driver"
"errors"
"fmt"
"math/rand"
"strings"
)
// FakeStmt is implementation of Stmt sql interfcae
type FakeStmt struct {
connection *FakeConn
q string // just for debugging SQL query generated by sql package
command string // String name of the command SELECT etc, taken as first word in the query
next *FakeStmt // used for returning multiple results.
closed bool // If connection closed already
colName []string // Names of columns in response
colType []string // Not used for now
placeholders int // Amount of passed args
}
// ColumnConverter returns a ValueConverter for the provided
// column index.
func (s *FakeStmt) ColumnConverter(idx int) driver.ValueConverter {
return driver.DefaultParameterConverter
}
// Close closes the connection
func (s *FakeStmt) Close() error {
// No connection added
if s.connection == nil {
panic("nil conn in FakeStmt.Close")
}
if s.connection.db == nil {
panic("in FakeStmt.Close, conn's db is nil (already closed)")
}
if !s.closed {
s.closed = true
}
if s.next != nil {
s.next.Close()
}
return nil
}
var errClosed = errors.New("fake_db_driver: statement has been closed")
// Exec executes a query that doesn't return rows, such
// as an INSERT or UPDATE.
//
// Deprecated: Drivers should implement StmtExecContext instead (or additionally).
func (s *FakeStmt) Exec(args []driver.Value) (driver.Result, error) {
panic("Using ExecContext")
}
// ExecContext executes a query that doesn't return rows, such
// as an INSERT or UPDATE.
func (s *FakeStmt) ExecContext(ctx context.Context, args []driver.NamedValue) (driver.Result, error) {
if s.closed {
return nil, errClosed
}
fResp := Catcher.FindResponse(s.q, args)
// To emulate any exception during query which returns rows
if fResp.Exceptions != nil && fResp.Exceptions.HookExecBadConnection != nil && fResp.Exceptions.HookExecBadConnection() {
return nil, driver.ErrBadConn
}
if fResp.Error != nil {
return nil, fResp.Error
}
if fResp.Callback != nil {
fResp.Callback(s.q, args)
}
switch s.command {
case "INSERT":
id := fResp.LastInsertID
if id == 0 {
id = rand.Int63()
}
res := NewFakeResult(id, 1)
return res, nil
case "UPDATE":
return driver.RowsAffected(fResp.RowsAffected), nil
case "DELETE":
return driver.RowsAffected(fResp.RowsAffected), nil
}
return nil, fmt.Errorf("unimplemented statement Exec command type of %q", s.command)
}
// Query executes a query that may return rows, such as a
// SELECT.
//
// Deprecated: Drivers should implement StmtQueryContext instead (or additionally).
func (s *FakeStmt) Query(args []driver.Value) (driver.Rows, error) {
panic("Use QueryContext")
}
// QueryContext executes a query that may return rows, such as a
// SELECT.
func (s *FakeStmt) QueryContext(ctx context.Context, args []driver.NamedValue) (driver.Rows, error) {
if s.closed {
return nil, errClosed
}
if len(args) > 0 {
// Replace all "?" to "%v" and replace them with the values after
for i := 0; i < len(args); i++ {
s.q = strings.Replace(s.q, "?", "%v", 1)
s.q = fmt.Sprintf(s.q, args[i].Value)
}
}
fResp := Catcher.FindResponse(s.q, args)
if fResp.Exceptions != nil && fResp.Exceptions.HookQueryBadConnection != nil && fResp.Exceptions.HookQueryBadConnection() {
return nil, driver.ErrBadConn
}
if fResp.Error != nil {
return nil, fResp.Error
}
resultRows := make([][]*row, 0, 1)
columnNames := make([]string, 0, 1)
columnTypes := make([][]string, 0, 1)
rows := []*row{}
// Check if we have such query in the map
colIndexes := make(map[string]int)
// Collecting column names from first record
if len(fResp.Response) > 0 {
for colName := range fResp.Response[0] {
colIndexes[colName] = len(columnNames)
columnNames = append(columnNames, colName)
}
}
// Extracting values from result according columns
for _, record := range fResp.Response {
oneRow := &row{cols: make([]interface{}, len(columnNames))}
for _, col := range columnNames {
oneRow.cols[colIndexes[col]] = record[col]
}
rows = append(rows, oneRow)
}
resultRows = append(resultRows, rows)
cursor := &RowsCursor{
posRow: -1,
rows: resultRows,
cols: columnNames,
colType: columnTypes, // TODO: implement support of that
errPos: -1,
closed: false,
}
if fResp.Callback != nil {
fResp.Callback(s.q, args)
}
return cursor, nil
}
// NumInput returns the number of placeholder parameters.
func (s *FakeStmt) NumInput() int {
return s.placeholders
}
// FakeTx implements Tx interface
type FakeTx struct {
c *FakeConn
}
// HookBadCommit is a hook to simulate broken connections
var HookBadCommit func() bool
// Commit commits the transaction
func (tx *FakeTx) Commit() error {
tx.c.currTx = nil
if HookBadCommit != nil && HookBadCommit() {
return driver.ErrBadConn
}
return nil
}
// HookBadRollback is a hook to simulate broken connections
var HookBadRollback func() bool
// Rollback rollbacks the transaction
func (tx *FakeTx) Rollback() error {
tx.c.currTx = nil
if HookBadRollback != nil && HookBadRollback() {
return driver.ErrBadConn
}
return nil
}