From b6e6667a3fe0f1285f4c39eb4fdae68811a2ee23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Garcia?= Date: Mon, 28 Jun 2021 17:35:46 -0300 Subject: [PATCH] Improve the names of some public types *breaking change* --- README.md | 16 ++--- contracts.go | 6 +- examples/crud/crud.go | 2 +- examples/example_service/example_service.go | 4 +- .../example_service/example_service_test.go | 14 ++-- examples/example_service/mocks.go | 70 +++++++++---------- ksql.go | 23 ++++-- ksql_test.go | 10 +-- mocks.go | 24 +++---- 9 files changed, 92 insertions(+), 77 deletions(-) diff --git a/README.md b/README.md index 0762c52..ebdcf7a 100644 --- a/README.md +++ b/README.md @@ -68,8 +68,8 @@ The current interface is as follows and we plan on keeping it with as little functions as possible, so don't expect many additions: ```go -// SQLProvider describes the public behavior of this ORM -type SQLProvider interface { +// Provider describes the public behavior of this ORM +type Provider interface { Insert(ctx context.Context, table Table, record interface{}) error Update(ctx context.Context, table Table, record interface{}) error Delete(ctx context.Context, table Table, idsOrRecords ...interface{}) error @@ -79,7 +79,7 @@ type SQLProvider interface { QueryChunks(ctx context.Context, parser ChunkParser) error Exec(ctx context.Context, query string, params ...interface{}) error - Transaction(ctx context.Context, fn func(SQLProvider) error) error + Transaction(ctx context.Context, fn func(Provider) error) error } ``` @@ -232,7 +232,7 @@ func main() { } // Making transactions: - err = db.Transaction(ctx, func(db ksql.SQLProvider) error { + err = db.Transaction(ctx, func(db ksql.Provider) error { var cris2 User err = db.QueryOne(ctx, &cris2, "SELECT * FROM users WHERE id = ?", cris.ID) if err != nil { @@ -372,10 +372,10 @@ Querying a single joined row: ```golang var row struct{ - User User `tablename:"u"` // (here the tablename must match the aliased tablename in the query) - Post Post `tablename:"p"` // (if no alias is used you should use the actual name of the table) + User User `tablename:"u"` // (here the tablename must match the aliased tablename in the query) + Post Post `tablename:"posts"` // (if no alias is used you should use the actual name of the table) } -err = db.QueryOne(ctx, &row, "FROM users as u JOIN posts as p ON u.id = p.user_id WHERE u.id = ?", userID) +err = db.QueryOne(ctx, &row, "FROM users as u JOIN posts ON u.id = posts.user_id WHERE u.id = ?", userID) if err != nil { panic(err.Error()) } @@ -521,13 +521,13 @@ make test ### TODO List -- Improve error messages - Add tests for tables using composite keys - Add support for serializing structs as other formats such as YAML - Update `kstructs.FillStructWith` to work with `json` tagged attributes - Make testing easier by exposing the connection strings in an .env file - Make testing easier by automatically creating the `ksql` database - Create a way for users to submit user defined dialects +- Improve error messages ### Optimization Oportunities diff --git a/contracts.go b/contracts.go index 26866de..8a9866f 100644 --- a/contracts.go +++ b/contracts.go @@ -14,8 +14,8 @@ var ErrRecordNotFound error = errors.Wrap(sql.ErrNoRows, "ksql: the query return // ErrAbortIteration ... var ErrAbortIteration error = fmt.Errorf("ksql: abort iteration, should only be used inside QueryChunks function") -// SQLProvider describes the public behavior of this ORM -type SQLProvider interface { +// Provider describes the public behavior of this ORM +type Provider interface { Insert(ctx context.Context, table Table, record interface{}) error Update(ctx context.Context, table Table, record interface{}) error Delete(ctx context.Context, table Table, idsOrRecords ...interface{}) error @@ -25,7 +25,7 @@ type SQLProvider interface { QueryChunks(ctx context.Context, parser ChunkParser) error Exec(ctx context.Context, query string, params ...interface{}) error - Transaction(ctx context.Context, fn func(SQLProvider) error) error + Transaction(ctx context.Context, fn func(Provider) error) error } // Table describes the required information for inserting, updating and diff --git a/examples/crud/crud.go b/examples/crud/crud.go index 20e78b0..6ef5df5 100644 --- a/examples/crud/crud.go +++ b/examples/crud/crud.go @@ -137,7 +137,7 @@ func main() { } // Making transactions: - err = db.Transaction(ctx, func(db ksql.SQLProvider) error { + err = db.Transaction(ctx, func(db ksql.Provider) error { var cris2 User err = db.QueryOne(ctx, &cris2, "SELECT * FROM users WHERE id = ?", cris.ID) if err != nil { diff --git a/examples/example_service/example_service.go b/examples/example_service/example_service.go index 94ca156..bbee499 100644 --- a/examples/example_service/example_service.go +++ b/examples/example_service/example_service.go @@ -40,12 +40,12 @@ type Address struct { // Service ... type Service struct { - db ksql.SQLProvider + db ksql.Provider streamChunkSize int } // NewUserService ... -func NewUserService(db ksql.SQLProvider) Service { +func NewUserService(db ksql.Provider) Service { return Service{ db: db, streamChunkSize: 100, diff --git a/examples/example_service/example_service_test.go b/examples/example_service/example_service_test.go index 007f1c0..f19d799 100644 --- a/examples/example_service/example_service_test.go +++ b/examples/example_service/example_service_test.go @@ -7,8 +7,8 @@ import ( gomock "github.com/golang/mock/gomock" "github.com/tj/assert" "github.com/vingarcia/ksql" - "github.com/vingarcia/ksql/nullable" "github.com/vingarcia/ksql/kstructs" + "github.com/vingarcia/ksql/nullable" ) func TestCreateUser(t *testing.T) { @@ -16,7 +16,7 @@ func TestCreateUser(t *testing.T) { controller := gomock.NewController(t) defer controller.Finish() - mockDB := NewMockSQLProvider(controller) + mockDB := NewMockProvider(controller) s := Service{ db: mockDB, @@ -42,7 +42,7 @@ func TestCreateUser(t *testing.T) { controller := gomock.NewController(t) defer controller.Finish() - mockDB := NewMockSQLProvider(controller) + mockDB := NewMockProvider(controller) s := Service{ db: mockDB, @@ -82,7 +82,7 @@ func TestUpdateUserScore(t *testing.T) { controller := gomock.NewController(t) defer controller.Finish() - mockDB := NewMockSQLProvider(controller) + mockDB := NewMockProvider(controller) s := Service{ db: mockDB, @@ -126,7 +126,7 @@ func TestListUsers(t *testing.T) { controller := gomock.NewController(t) defer controller.Finish() - mockDB := NewMockSQLProvider(controller) + mockDB := NewMockProvider(controller) s := Service{ db: mockDB, @@ -188,7 +188,7 @@ func TestStreamAllUsers(t *testing.T) { controller := gomock.NewController(t) defer controller.Finish() - mockDB := NewMockSQLProvider(controller) + mockDB := NewMockProvider(controller) s := Service{ db: mockDB, @@ -260,7 +260,7 @@ func TestDeleteUser(t *testing.T) { controller := gomock.NewController(t) defer controller.Finish() - mockDB := NewMockSQLProvider(controller) + mockDB := NewMockProvider(controller) s := Service{ db: mockDB, diff --git a/examples/example_service/mocks.go b/examples/example_service/mocks.go index c170490..1307ab2 100644 --- a/examples/example_service/mocks.go +++ b/examples/example_service/mocks.go @@ -12,31 +12,31 @@ import ( ksql "github.com/vingarcia/ksql" ) -// MockSQLProvider is a mock of SQLProvider interface. -type MockSQLProvider struct { +// MockProvider is a mock of Provider interface. +type MockProvider struct { ctrl *gomock.Controller - recorder *MockSQLProviderMockRecorder + recorder *MockProviderMockRecorder } -// MockSQLProviderMockRecorder is the mock recorder for MockSQLProvider. -type MockSQLProviderMockRecorder struct { - mock *MockSQLProvider +// MockProviderMockRecorder is the mock recorder for MockProvider. +type MockProviderMockRecorder struct { + mock *MockProvider } -// NewMockSQLProvider creates a new mock instance. -func NewMockSQLProvider(ctrl *gomock.Controller) *MockSQLProvider { - mock := &MockSQLProvider{ctrl: ctrl} - mock.recorder = &MockSQLProviderMockRecorder{mock} +// NewMockProvider creates a new mock instance. +func NewMockProvider(ctrl *gomock.Controller) *MockProvider { + mock := &MockProvider{ctrl: ctrl} + mock.recorder = &MockProviderMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockSQLProvider) EXPECT() *MockSQLProviderMockRecorder { +func (m *MockProvider) EXPECT() *MockProviderMockRecorder { return m.recorder } // Delete mocks base method. -func (m *MockSQLProvider) Delete(ctx context.Context, table ksql.Table, idsOrRecords ...interface{}) error { +func (m *MockProvider) Delete(ctx context.Context, table ksql.Table, idsOrRecords ...interface{}) error { m.ctrl.T.Helper() varargs := []interface{}{ctx, table} for _, a := range idsOrRecords { @@ -48,14 +48,14 @@ func (m *MockSQLProvider) Delete(ctx context.Context, table ksql.Table, idsOrRec } // Delete indicates an expected call of Delete. -func (mr *MockSQLProviderMockRecorder) Delete(ctx, table interface{}, idsOrRecords ...interface{}) *gomock.Call { +func (mr *MockProviderMockRecorder) Delete(ctx, table interface{}, idsOrRecords ...interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]interface{}{ctx, table}, idsOrRecords...) - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockSQLProvider)(nil).Delete), varargs...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockProvider)(nil).Delete), varargs...) } // Exec mocks base method. -func (m *MockSQLProvider) Exec(ctx context.Context, query string, params ...interface{}) error { +func (m *MockProvider) Exec(ctx context.Context, query string, params ...interface{}) error { m.ctrl.T.Helper() varargs := []interface{}{ctx, query} for _, a := range params { @@ -67,14 +67,14 @@ func (m *MockSQLProvider) Exec(ctx context.Context, query string, params ...inte } // Exec indicates an expected call of Exec. -func (mr *MockSQLProviderMockRecorder) Exec(ctx, query interface{}, params ...interface{}) *gomock.Call { +func (mr *MockProviderMockRecorder) Exec(ctx, query interface{}, params ...interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]interface{}{ctx, query}, params...) - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Exec", reflect.TypeOf((*MockSQLProvider)(nil).Exec), varargs...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Exec", reflect.TypeOf((*MockProvider)(nil).Exec), varargs...) } // Insert mocks base method. -func (m *MockSQLProvider) Insert(ctx context.Context, table ksql.Table, record interface{}) error { +func (m *MockProvider) Insert(ctx context.Context, table ksql.Table, record interface{}) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Insert", ctx, table, record) ret0, _ := ret[0].(error) @@ -82,13 +82,13 @@ func (m *MockSQLProvider) Insert(ctx context.Context, table ksql.Table, record i } // Insert indicates an expected call of Insert. -func (mr *MockSQLProviderMockRecorder) Insert(ctx, table, record interface{}) *gomock.Call { +func (mr *MockProviderMockRecorder) Insert(ctx, table, record interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Insert", reflect.TypeOf((*MockSQLProvider)(nil).Insert), ctx, table, record) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Insert", reflect.TypeOf((*MockProvider)(nil).Insert), ctx, table, record) } // Query mocks base method. -func (m *MockSQLProvider) Query(ctx context.Context, records interface{}, query string, params ...interface{}) error { +func (m *MockProvider) Query(ctx context.Context, records interface{}, query string, params ...interface{}) error { m.ctrl.T.Helper() varargs := []interface{}{ctx, records, query} for _, a := range params { @@ -100,14 +100,14 @@ func (m *MockSQLProvider) Query(ctx context.Context, records interface{}, query } // Query indicates an expected call of Query. -func (mr *MockSQLProviderMockRecorder) Query(ctx, records, query interface{}, params ...interface{}) *gomock.Call { +func (mr *MockProviderMockRecorder) Query(ctx, records, query interface{}, params ...interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]interface{}{ctx, records, query}, params...) - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Query", reflect.TypeOf((*MockSQLProvider)(nil).Query), varargs...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Query", reflect.TypeOf((*MockProvider)(nil).Query), varargs...) } // QueryChunks mocks base method. -func (m *MockSQLProvider) QueryChunks(ctx context.Context, parser ksql.ChunkParser) error { +func (m *MockProvider) QueryChunks(ctx context.Context, parser ksql.ChunkParser) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "QueryChunks", ctx, parser) ret0, _ := ret[0].(error) @@ -115,13 +115,13 @@ func (m *MockSQLProvider) QueryChunks(ctx context.Context, parser ksql.ChunkPars } // QueryChunks indicates an expected call of QueryChunks. -func (mr *MockSQLProviderMockRecorder) QueryChunks(ctx, parser interface{}) *gomock.Call { +func (mr *MockProviderMockRecorder) QueryChunks(ctx, parser interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "QueryChunks", reflect.TypeOf((*MockSQLProvider)(nil).QueryChunks), ctx, parser) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "QueryChunks", reflect.TypeOf((*MockProvider)(nil).QueryChunks), ctx, parser) } // QueryOne mocks base method. -func (m *MockSQLProvider) QueryOne(ctx context.Context, record interface{}, query string, params ...interface{}) error { +func (m *MockProvider) QueryOne(ctx context.Context, record interface{}, query string, params ...interface{}) error { m.ctrl.T.Helper() varargs := []interface{}{ctx, record, query} for _, a := range params { @@ -133,14 +133,14 @@ func (m *MockSQLProvider) QueryOne(ctx context.Context, record interface{}, quer } // QueryOne indicates an expected call of QueryOne. -func (mr *MockSQLProviderMockRecorder) QueryOne(ctx, record, query interface{}, params ...interface{}) *gomock.Call { +func (mr *MockProviderMockRecorder) QueryOne(ctx, record, query interface{}, params ...interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]interface{}{ctx, record, query}, params...) - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "QueryOne", reflect.TypeOf((*MockSQLProvider)(nil).QueryOne), varargs...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "QueryOne", reflect.TypeOf((*MockProvider)(nil).QueryOne), varargs...) } // Transaction mocks base method. -func (m *MockSQLProvider) Transaction(ctx context.Context, fn func(ksql.SQLProvider) error) error { +func (m *MockProvider) Transaction(ctx context.Context, fn func(ksql.Provider) error) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Transaction", ctx, fn) ret0, _ := ret[0].(error) @@ -148,13 +148,13 @@ func (m *MockSQLProvider) Transaction(ctx context.Context, fn func(ksql.SQLProvi } // Transaction indicates an expected call of Transaction. -func (mr *MockSQLProviderMockRecorder) Transaction(ctx, fn interface{}) *gomock.Call { +func (mr *MockProviderMockRecorder) Transaction(ctx, fn interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Transaction", reflect.TypeOf((*MockSQLProvider)(nil).Transaction), ctx, fn) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Transaction", reflect.TypeOf((*MockProvider)(nil).Transaction), ctx, fn) } // Update mocks base method. -func (m *MockSQLProvider) Update(ctx context.Context, table ksql.Table, record interface{}) error { +func (m *MockProvider) Update(ctx context.Context, table ksql.Table, record interface{}) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Update", ctx, table, record) ret0, _ := ret[0].(error) @@ -162,7 +162,7 @@ func (m *MockSQLProvider) Update(ctx context.Context, table ksql.Table, record i } // Update indicates an expected call of Update. -func (mr *MockSQLProviderMockRecorder) Update(ctx, table, record interface{}) *gomock.Call { +func (mr *MockProviderMockRecorder) Update(ctx, table, record interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockSQLProvider)(nil).Update), ctx, table, record) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockProvider)(nil).Update), ctx, table, record) } diff --git a/ksql.go b/ksql.go index b359b36..1b79e15 100644 --- a/ksql.go +++ b/ksql.go @@ -22,14 +22,19 @@ func init() { // DB represents the ksql client responsible for // interfacing with the "database/sql" package implementing -// the KissSQL interface `SQLProvider`. +// the KissSQL interface `ksql.Provider`. type DB struct { driver string dialect dialect - db sqlProvider + db DBAdapter } -type sqlProvider interface { +// DBAdapter is minimalistic interface to decouple our implementation +// from database/sql, i.e. if any struct implements the functions below +// with the exact same semantic as the sql package it will work with ksql. +// +// To create a new client using this adapter use ksql.NewWithDB() +type DBAdapter interface { ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error) QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error) } @@ -61,6 +66,16 @@ func New( db.SetMaxOpenConns(config.MaxOpenConns) + return newWithDB(db, dbDriver, connectionString) +} + +// NewWithDB allows the user to insert a custom implementation +// of the DBAdapter interface +func newWithDB( + db DBAdapter, + dbDriver string, + connectionString string, +) (DB, error) { dialect := supportedDialects[dbDriver] if dialect == nil { return DB{}, fmt.Errorf("unsupported driver `%s`", dbDriver) @@ -731,7 +746,7 @@ func (c DB) Exec(ctx context.Context, query string, params ...interface{}) error } // Transaction just runs an SQL command on the database returning no rows. -func (c DB) Transaction(ctx context.Context, fn func(SQLProvider) error) error { +func (c DB) Transaction(ctx context.Context, fn func(Provider) error) error { switch db := c.db.(type) { case *sql.Tx: return fn(c) diff --git a/ksql_test.go b/ksql_test.go index eccec01..45fbc15 100644 --- a/ksql_test.go +++ b/ksql_test.go @@ -1588,7 +1588,7 @@ func TestTransaction(t *testing.T) { _ = c.Insert(ctx, UsersTable, &User{Name: "User2"}) var users []User - err = c.Transaction(ctx, func(db SQLProvider) error { + err = c.Transaction(ctx, func(db Provider) error { db.Query(ctx, &users, "SELECT * FROM users ORDER BY id ASC") return nil }) @@ -1616,7 +1616,7 @@ func TestTransaction(t *testing.T) { _ = c.Insert(ctx, UsersTable, &u1) _ = c.Insert(ctx, UsersTable, &u2) - err = c.Transaction(ctx, func(db SQLProvider) error { + err = c.Transaction(ctx, func(db Provider) error { err = db.Insert(ctx, UsersTable, &User{Name: "User3"}) assert.Equal(t, nil, err) err = db.Insert(ctx, UsersTable, &User{Name: "User4"}) @@ -1878,7 +1878,7 @@ func shiftErrSlice(errs *[]error) error { return err } -func getUsersByID(dbi sqlProvider, dialect dialect, resultsPtr *[]User, ids ...uint) error { +func getUsersByID(dbi DBAdapter, dialect dialect, resultsPtr *[]User, ids ...uint) error { db := dbi.(*sql.DB) placeholders := make([]string, len(ids)) @@ -1920,7 +1920,7 @@ func getUsersByID(dbi sqlProvider, dialect dialect, resultsPtr *[]User, ids ...u return nil } -func getUserByID(dbi sqlProvider, dialect dialect, result *User, id uint) error { +func getUserByID(dbi DBAdapter, dialect dialect, result *User, id uint) error { db := dbi.(*sql.DB) row := db.QueryRow(`SELECT id, name, age, address FROM users WHERE id=`+dialect.Placeholder(0), id) @@ -1941,7 +1941,7 @@ func getUserByID(dbi sqlProvider, dialect dialect, result *User, id uint) error return nil } -func getUserByName(dbi sqlProvider, dialect dialect, result *User, name string) error { +func getUserByName(dbi DBAdapter, dialect dialect, result *User, name string) error { db := dbi.(*sql.DB) row := db.QueryRow(`SELECT id, name, age, address FROM users WHERE name=`+dialect.Placeholder(0), name) diff --git a/mocks.go b/mocks.go index 8fc7321..97911cd 100644 --- a/mocks.go +++ b/mocks.go @@ -2,10 +2,10 @@ package ksql import "context" -var _ SQLProvider = MockSQLProvider{} +var _ Provider = Mock{} -// MockSQLProvider ... -type MockSQLProvider struct { +// Mock ... +type Mock struct { InsertFn func(ctx context.Context, table Table, record interface{}) error UpdateFn func(ctx context.Context, table Table, record interface{}) error DeleteFn func(ctx context.Context, table Table, ids ...interface{}) error @@ -15,45 +15,45 @@ type MockSQLProvider struct { QueryChunksFn func(ctx context.Context, parser ChunkParser) error ExecFn func(ctx context.Context, query string, params ...interface{}) error - TransactionFn func(ctx context.Context, fn func(db SQLProvider) error) error + TransactionFn func(ctx context.Context, fn func(db Provider) error) error } // Insert ... -func (m MockSQLProvider) Insert(ctx context.Context, table Table, record interface{}) error { +func (m Mock) Insert(ctx context.Context, table Table, record interface{}) error { return m.InsertFn(ctx, table, record) } // Update ... -func (m MockSQLProvider) Update(ctx context.Context, table Table, record interface{}) error { +func (m Mock) Update(ctx context.Context, table Table, record interface{}) error { return m.UpdateFn(ctx, table, record) } // Delete ... -func (m MockSQLProvider) Delete(ctx context.Context, table Table, ids ...interface{}) error { +func (m Mock) Delete(ctx context.Context, table Table, ids ...interface{}) error { return m.DeleteFn(ctx, table, ids...) } // Query ... -func (m MockSQLProvider) Query(ctx context.Context, records interface{}, query string, params ...interface{}) error { +func (m Mock) Query(ctx context.Context, records interface{}, query string, params ...interface{}) error { return m.QueryFn(ctx, records, query, params...) } // QueryOne ... -func (m MockSQLProvider) QueryOne(ctx context.Context, record interface{}, query string, params ...interface{}) error { +func (m Mock) QueryOne(ctx context.Context, record interface{}, query string, params ...interface{}) error { return m.QueryOneFn(ctx, record, query, params...) } // QueryChunks ... -func (m MockSQLProvider) QueryChunks(ctx context.Context, parser ChunkParser) error { +func (m Mock) QueryChunks(ctx context.Context, parser ChunkParser) error { return m.QueryChunksFn(ctx, parser) } // Exec ... -func (m MockSQLProvider) Exec(ctx context.Context, query string, params ...interface{}) error { +func (m Mock) Exec(ctx context.Context, query string, params ...interface{}) error { return m.ExecFn(ctx, query, params...) } // Transaction ... -func (m MockSQLProvider) Transaction(ctx context.Context, fn func(db SQLProvider) error) error { +func (m Mock) Transaction(ctx context.Context, fn func(db Provider) error) error { return m.TransactionFn(ctx, fn) }