Files
go-mocket/stmt.go
T
Alexandre Vallières-LagacéandAlexandre Vallières-Lagacé 0f12e25bc0 Fixed linter errors from goreportcard
Fixed simple naming, comment conventions
Fixed pointer references to a struc where there was also a pointer. Govet complain was right.
2018-10-29 09:17:11 -04:00

205 lines
5.1 KiB
Go

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] {
columnNames = append(columnNames, colName)
colIndexes[colName] = len(columnNames) - 1
}
}
// 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
}