mirror of
https://github.com/wahyd4/go-mocket.git
synced 2026-08-09 04:46:54 +10:00
allows to put different types of data. For example time.Time and represent it correctly in output struct when using mocked replies
202 lines
5.0 KiB
Go
202 lines
5.0 KiB
Go
package go_mocket
|
|
|
|
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
|
|
}
|
|
|
|
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 (smt *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 (smt *FakeStmt) ExecContext(ctx context.Context, args []driver.NamedValue) (driver.Result, error) {
|
|
if smt.closed {
|
|
return nil, errClosed
|
|
}
|
|
|
|
fResp := Catcher.FindResponse(smt.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(smt.q, args)
|
|
}
|
|
|
|
switch smt.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", smt.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 (smt *FakeStmt) QueryContext(ctx context.Context, args []driver.NamedValue) (driver.Rows, error) {
|
|
|
|
if smt.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++ {
|
|
smt.q = strings.Replace(smt.q, "?", "%v", 1)
|
|
smt.q = fmt.Sprintf(smt.q, args[i].Value)
|
|
}
|
|
}
|
|
|
|
fResp := Catcher.FindResponse(smt.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(smt.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
|
|
}
|
|
|
|
// hook to simulate broken connections
|
|
var HookBadCommit func() bool
|
|
|
|
func (tx *FakeTx) Commit() error {
|
|
tx.c.currTx = nil
|
|
if HookBadCommit != nil && HookBadCommit() {
|
|
return driver.ErrBadConn
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// hook to simulate broken connections
|
|
var HookBadRollback func() bool
|
|
|
|
func (tx *FakeTx) Rollback() error {
|
|
tx.c.currTx = nil
|
|
if HookBadRollback != nil && HookBadRollback() {
|
|
return driver.ErrBadConn
|
|
}
|
|
return nil
|
|
}
|