├── gen.go ├── context.go ├── go.mod ├── pool.go ├── runner.go ├── LICENSE ├── query.go ├── runner_mocks_test.go ├── README.md ├── context_test.go ├── runner_test.go ├── go.sum ├── query_test.go └── mocks_test.go /gen.go: -------------------------------------------------------------------------------- 1 | package pgxatomic 2 | 3 | //go:generate mockgen -package pgxatomic -destination mocks_test.go github.com/jackc/pgx/v5 Rows,Row,Tx 4 | //go:generate mockgen -package pgxatomic -source runner.go -destination runner_mocks_test.go txStarter 5 | -------------------------------------------------------------------------------- /context.go: -------------------------------------------------------------------------------- 1 | package pgxatomic 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/jackc/pgx/v5" 7 | ) 8 | 9 | type txKey struct{} 10 | 11 | // WithTx sets pgx.Tx into context. 12 | func WithTx(ctx context.Context, tx pgx.Tx) context.Context { 13 | return context.WithValue(ctx, txKey{}, tx) 14 | } 15 | 16 | // TxFromContext return pgx.Tx from context or nil if not found. 17 | func TxFromContext(ctx context.Context) pgx.Tx { 18 | if ctx == nil { 19 | return nil 20 | } 21 | if tx, ok := ctx.Value(txKey{}).(pgx.Tx); ok { 22 | return tx 23 | } 24 | return nil 25 | } 26 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/ysomad/pgxatomic 2 | 3 | go 1.25 4 | 5 | require ( 6 | github.com/jackc/pgx/v5 v5.7.6 7 | github.com/stretchr/testify v1.9.0 8 | go.uber.org/mock v0.6.0 9 | ) 10 | 11 | require ( 12 | github.com/davecgh/go-spew v1.1.1 // indirect 13 | github.com/jackc/pgpassfile v1.0.0 // indirect 14 | github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect 15 | github.com/jackc/puddle/v2 v2.2.2 // indirect 16 | github.com/kr/text v0.2.0 // indirect 17 | github.com/pmezard/go-difflib v1.0.0 // indirect 18 | github.com/rogpeppe/go-internal v1.14.1 // indirect 19 | golang.org/x/crypto v0.43.0 // indirect 20 | golang.org/x/sync v0.17.0 // indirect 21 | golang.org/x/text v0.30.0 // indirect 22 | gopkg.in/yaml.v3 v3.0.1 // indirect 23 | ) 24 | -------------------------------------------------------------------------------- /pool.go: -------------------------------------------------------------------------------- 1 | package pgxatomic 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | 7 | "github.com/jackc/pgx/v5" 8 | "github.com/jackc/pgx/v5/pgconn" 9 | "github.com/jackc/pgx/v5/pgxpool" 10 | ) 11 | 12 | // Pool wraps pgxpool.Pool query methods with pgxatomic corresponding functions 13 | // which injects pgx.Tx into context. 14 | type Pool struct { 15 | p *pgxpool.Pool 16 | } 17 | 18 | func NewPool(p *pgxpool.Pool) (Pool, error) { 19 | if p == nil { 20 | return Pool{}, errors.New("pgxatomic: pool cannot be nil") 21 | } 22 | return Pool{p: p}, nil 23 | } 24 | 25 | func (p Pool) Query(ctx context.Context, sql string, args ...any) (pgx.Rows, error) { 26 | return Query(ctx, p.p, sql, args...) 27 | } 28 | 29 | func (p Pool) QueryRow(ctx context.Context, sql string, args ...any) pgx.Row { 30 | return QueryRow(ctx, p.p, sql, args...) 31 | } 32 | 33 | func (p Pool) Exec(ctx context.Context, sql string, args ...any) (pgconn.CommandTag, error) { 34 | return Exec(ctx, p.p, sql, args...) 35 | } 36 | -------------------------------------------------------------------------------- /runner.go: -------------------------------------------------------------------------------- 1 | package pgxatomic 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | 7 | "github.com/jackc/pgx/v5" 8 | ) 9 | 10 | type txStarter interface { 11 | BeginTx(ctx context.Context, txOptions pgx.TxOptions) (pgx.Tx, error) 12 | } 13 | 14 | // Runner starts transaction in Run method by wrapping txFunc using db, 15 | // pgx.Conn and pgxpool.Pool implements db. 16 | type Runner struct { 17 | db txStarter 18 | opts pgx.TxOptions 19 | } 20 | 21 | func NewRunner(db txStarter, opts pgx.TxOptions) (Runner, error) { 22 | if db == nil { 23 | return Runner{}, errors.New("pgxatomic: db cannot be nil") 24 | } 25 | 26 | return Runner{ 27 | db: db, 28 | opts: opts, 29 | }, nil 30 | } 31 | 32 | // Run wraps txFunc in pgx.BeginTxFunc with injected pgx.Tx into context and runs it. 33 | func (r Runner) Run(ctx context.Context, txFunc func(ctx context.Context) error) error { 34 | return pgx.BeginTxFunc(ctx, r.db, r.opts, func(tx pgx.Tx) error { 35 | return txFunc(WithTx(ctx, tx)) 36 | }) 37 | } 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Malykh Aleksei 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /query.go: -------------------------------------------------------------------------------- 1 | package pgxatomic 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/jackc/pgx/v5" 7 | "github.com/jackc/pgx/v5/pgconn" 8 | ) 9 | 10 | type querier interface { 11 | Query(ctx context.Context, sql string, args ...any) (pgx.Rows, error) 12 | } 13 | 14 | // Query is a wrapper around pgx Query method. 15 | func Query(ctx context.Context, db querier, sql string, args ...any) (pgx.Rows, error) { 16 | if tx := TxFromContext(ctx); tx != nil { 17 | return tx.Query(ctx, sql, args...) 18 | } 19 | return db.Query(ctx, sql, args...) 20 | } 21 | 22 | type executor interface { 23 | Exec(ctx context.Context, sql string, args ...any) (pgconn.CommandTag, error) 24 | } 25 | 26 | // Exec is a wrapper around pgx Exec method. 27 | func Exec(ctx context.Context, db executor, sql string, args ...any) (pgconn.CommandTag, error) { 28 | if tx := TxFromContext(ctx); tx != nil { 29 | return tx.Exec(ctx, sql, args...) 30 | } 31 | return db.Exec(ctx, sql, args...) 32 | } 33 | 34 | type queryRower interface { 35 | QueryRow(ctx context.Context, sql string, args ...any) pgx.Row 36 | } 37 | 38 | // QueryRow is a wrapper around pgx QueryRow method. 39 | func QueryRow(ctx context.Context, db queryRower, sql string, args ...any) pgx.Row { 40 | if tx := TxFromContext(ctx); tx != nil { 41 | return tx.QueryRow(ctx, sql, args...) 42 | } 43 | return db.QueryRow(ctx, sql, args...) 44 | } 45 | -------------------------------------------------------------------------------- /runner_mocks_test.go: -------------------------------------------------------------------------------- 1 | // Code generated by MockGen. DO NOT EDIT. 2 | // Source: runner.go 3 | // 4 | // Generated by this command: 5 | // 6 | // mockgen -package pgxatomic -source runner.go -destination runner_mocks_test.go txStarter 7 | // 8 | 9 | // Package pgxatomic is a generated GoMock package. 10 | package pgxatomic 11 | 12 | import ( 13 | context "context" 14 | reflect "reflect" 15 | 16 | pgx "github.com/jackc/pgx/v5" 17 | gomock "go.uber.org/mock/gomock" 18 | ) 19 | 20 | // MocktxStarter is a mock of txStarter interface. 21 | type MocktxStarter struct { 22 | ctrl *gomock.Controller 23 | recorder *MocktxStarterMockRecorder 24 | isgomock struct{} 25 | } 26 | 27 | // MocktxStarterMockRecorder is the mock recorder for MocktxStarter. 28 | type MocktxStarterMockRecorder struct { 29 | mock *MocktxStarter 30 | } 31 | 32 | // NewMocktxStarter creates a new mock instance. 33 | func NewMocktxStarter(ctrl *gomock.Controller) *MocktxStarter { 34 | mock := &MocktxStarter{ctrl: ctrl} 35 | mock.recorder = &MocktxStarterMockRecorder{mock} 36 | return mock 37 | } 38 | 39 | // EXPECT returns an object that allows the caller to indicate expected use. 40 | func (m *MocktxStarter) EXPECT() *MocktxStarterMockRecorder { 41 | return m.recorder 42 | } 43 | 44 | // BeginTx mocks base method. 45 | func (m *MocktxStarter) BeginTx(ctx context.Context, txOptions pgx.TxOptions) (pgx.Tx, error) { 46 | m.ctrl.T.Helper() 47 | ret := m.ctrl.Call(m, "BeginTx", ctx, txOptions) 48 | ret0, _ := ret[0].(pgx.Tx) 49 | ret1, _ := ret[1].(error) 50 | return ret0, ret1 51 | } 52 | 53 | // BeginTx indicates an expected call of BeginTx. 54 | func (mr *MocktxStarterMockRecorder) BeginTx(ctx, txOptions any) *gomock.Call { 55 | mr.mock.ctrl.T.Helper() 56 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BeginTx", reflect.TypeOf((*MocktxStarter)(nil).BeginTx), ctx, txOptions) 57 | } 58 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pgxatomic 2 | 3 | A library for managing PostgreSQL transactions through context propagation using the [pgx](https://github.com/jackc/pgx) driver. 4 | 5 | pgxatomic allows you to pass transactions via `context.Context`, providing cleaner separation between business logic and transaction management. Repository and service layers work with contexts while transaction boundaries are controlled at higher levels. 6 | 7 | ![schema](https://i.imgur.com/RpsfuBb.jpg) 8 | 9 | ## Installation 10 | 11 | ```bash 12 | go get github.com/ysomad/pgxatomic 13 | ``` 14 | 15 | ## Usage 16 | 17 | ### Repository layer 18 | 19 | Use `pgxatomic.Pool` in your repository. It wraps `pgxpool.Pool` and automatically uses transactions from context when available. 20 | 21 | ```go 22 | type orderRepo struct { 23 | pool *pgxatomic.Pool 24 | } 25 | 26 | type order struct { 27 | ID uuid.UUID 28 | Cost int 29 | } 30 | 31 | func (r *orderRepo) Insert(ctx context.Context, cost int) order { 32 | rows, _ := r.pool.Query(ctx, "INSERT INTO orders(cost) VALUES ($1) RETURNING id, cost", cost) 33 | o, _ := pgx.CollectOneRow(rows, pgx.RowToStructByPos[order]) 34 | return o 35 | } 36 | ``` 37 | 38 | Alternatively, use `Query`, `QueryRow`, and `Exec` functions directly without the pool wrapper. 39 | 40 | ### Transaction management 41 | 42 | Use `Runner` to execute operations within a transaction. The transaction propagates automatically through context. 43 | 44 | ```go 45 | conf, _ := pgxpool.ParseConfig("postgres://user:pass@localhost:5432/postgres") 46 | pool, _ := pgxpool.NewWithConfig(context.Background(), conf) 47 | 48 | runner, _ := pgxatomic.NewRunner(pool, pgx.TxOptions{}) 49 | 50 | _ = runner.Run(context.Background(), func(txCtx context.Context) error { 51 | _ = orderService.Create(txCtx) 52 | _ = balanceService.Withdraw(txCtx) 53 | return nil 54 | }) 55 | ``` 56 | 57 | All operations within `Run` use the same transaction. If the function returns an error, the transaction rolls back. Otherwise, it commits. 58 | 59 | Note: Error handling is omitted for brevity. Handle errors appropriately in production code. 60 | 61 | ## References 62 | 63 | - [Clean transactions in Golang hexagon](https://www.kaznacheev.me/posts/en/clean-transactions-in-hexagon) 64 | -------------------------------------------------------------------------------- /context_test.go: -------------------------------------------------------------------------------- 1 | package pgxatomic 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/jackc/pgx/v5" 8 | "github.com/stretchr/testify/assert" 9 | "go.uber.org/mock/gomock" 10 | ) 11 | 12 | type testKey string 13 | 14 | func TestWithTx(t *testing.T) { 15 | ctrl := gomock.NewController(t) 16 | defer ctrl.Finish() 17 | 18 | tx1 := NewMockTx(ctrl) 19 | tx2 := NewMockTx(ctrl) 20 | 21 | tests := []struct { 22 | name string 23 | ctx context.Context 24 | tx pgx.Tx 25 | }{ 26 | { 27 | name: "nil tx", 28 | ctx: context.Background(), 29 | tx: nil, 30 | }, 31 | { 32 | name: "tx1", 33 | ctx: context.Background(), 34 | tx: tx1, 35 | }, 36 | { 37 | name: "tx2", 38 | ctx: context.TODO(), 39 | tx: tx2, 40 | }, 41 | { 42 | name: "override", 43 | ctx: context.WithValue(context.Background(), txKey{}, "not-a-tx"), 44 | tx: tx1, 45 | }, 46 | { 47 | name: "with other key", 48 | ctx: context.WithValue(context.Background(), testKey("other"), "value"), 49 | tx: tx2, 50 | }, 51 | } 52 | for _, tt := range tests { 53 | t.Run(tt.name, func(t *testing.T) { 54 | gotCtx := WithTx(tt.ctx, tt.tx) 55 | assert.NotNil(t, gotCtx) 56 | 57 | if tt.tx != nil { 58 | gotTx, ok := gotCtx.Value(txKey{}).(pgx.Tx) 59 | assert.True(t, ok) 60 | assert.Equal(t, tt.tx, gotTx) 61 | } 62 | }) 63 | } 64 | } 65 | 66 | func TestTxFromContext(t *testing.T) { 67 | ctrl := gomock.NewController(t) 68 | defer ctrl.Finish() 69 | 70 | tx1 := NewMockTx(ctrl) 71 | tx2 := NewMockTx(ctrl) 72 | 73 | tests := []struct { 74 | name string 75 | ctx context.Context 76 | want pgx.Tx 77 | }{ 78 | { 79 | name: "nil tx", 80 | ctx: context.WithValue(context.Background(), txKey{}, nil), 81 | want: nil, 82 | }, 83 | { 84 | name: "tx1", 85 | ctx: context.WithValue(context.Background(), txKey{}, tx1), 86 | want: tx1, 87 | }, 88 | { 89 | name: "tx2", 90 | ctx: context.WithValue(context.TODO(), txKey{}, tx2), 91 | want: tx2, 92 | }, 93 | { 94 | name: "not found", 95 | ctx: context.Background(), 96 | want: nil, 97 | }, 98 | { 99 | name: "other key", 100 | ctx: context.WithValue(context.Background(), testKey("other"), "value"), 101 | want: nil, 102 | }, 103 | { 104 | name: "not tx", 105 | ctx: context.WithValue(context.Background(), txKey{}, "not-a-tx"), 106 | want: nil, 107 | }, 108 | { 109 | name: "nil context", 110 | ctx: nil, 111 | want: nil, 112 | }, 113 | } 114 | for _, tt := range tests { 115 | t.Run(tt.name, func(t *testing.T) { 116 | got := TxFromContext(tt.ctx) 117 | assert.Equal(t, tt.want, got) 118 | }) 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /runner_test.go: -------------------------------------------------------------------------------- 1 | package pgxatomic 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "testing" 7 | 8 | "github.com/jackc/pgx/v5" 9 | "github.com/stretchr/testify/assert" 10 | "go.uber.org/mock/gomock" 11 | ) 12 | 13 | var errTest = errors.New("test error") 14 | 15 | func TestNewRunner(t *testing.T) { 16 | ctrl := gomock.NewController(t) 17 | defer ctrl.Finish() 18 | 19 | mockDB := NewMocktxStarter(ctrl) 20 | 21 | type args struct { 22 | db txStarter 23 | opts pgx.TxOptions 24 | } 25 | 26 | tests := []struct { 27 | name string 28 | args args 29 | want Runner 30 | assertion assert.ErrorAssertionFunc 31 | }{ 32 | { 33 | name: "empty", 34 | args: args{}, 35 | want: Runner{}, 36 | assertion: assert.Error, 37 | }, 38 | { 39 | name: "ok", 40 | args: args{ 41 | db: mockDB, 42 | opts: pgx.TxOptions{IsoLevel: pgx.ReadCommitted}, 43 | }, 44 | want: Runner{ 45 | db: mockDB, 46 | opts: pgx.TxOptions{IsoLevel: pgx.ReadCommitted}, 47 | }, 48 | assertion: assert.NoError, 49 | }, 50 | { 51 | name: "ok default options", 52 | args: args{db: mockDB}, 53 | want: Runner{db: mockDB}, 54 | assertion: assert.NoError, 55 | }, 56 | } 57 | 58 | for _, tt := range tests { 59 | t.Run(tt.name, func(t *testing.T) { 60 | got, err := NewRunner(tt.args.db, tt.args.opts) 61 | tt.assertion(t, err) 62 | assert.Equal(t, tt.want, got) 63 | }) 64 | } 65 | } 66 | 67 | func TestRun_Commit(t *testing.T) { 68 | ctrl := gomock.NewController(t) 69 | defer ctrl.Finish() 70 | 71 | mockDB := NewMocktxStarter(ctrl) 72 | mockTx := NewMockTx(ctrl) 73 | 74 | mockDB.EXPECT().BeginTx(gomock.Any(), gomock.Any()).Return(mockTx, nil) 75 | mockTx.EXPECT().Commit(gomock.Any()).Return(nil) 76 | mockTx.EXPECT().Rollback(gomock.Any()).Return(nil).AnyTimes() 77 | 78 | runner, err := NewRunner(mockDB, pgx.TxOptions{}) 79 | assert.NoError(t, err) 80 | 81 | err = runner.Run(context.Background(), func(ctx context.Context) error { 82 | tx := TxFromContext(ctx) 83 | assert.Equal(t, mockTx, tx) 84 | return nil 85 | }) 86 | assert.NoError(t, err) 87 | } 88 | 89 | func TestRun_Rollback(t *testing.T) { 90 | ctrl := gomock.NewController(t) 91 | defer ctrl.Finish() 92 | 93 | mockDB := NewMocktxStarter(ctrl) 94 | mockTx := NewMockTx(ctrl) 95 | 96 | mockDB.EXPECT().BeginTx(gomock.Any(), gomock.Any()).Return(mockTx, nil) 97 | mockTx.EXPECT().Rollback(gomock.Any()).Return(nil).AnyTimes() 98 | 99 | runner, err := NewRunner(mockDB, pgx.TxOptions{}) 100 | assert.NoError(t, err) 101 | 102 | err = runner.Run(context.Background(), func(ctx context.Context) error { 103 | return errTest 104 | }) 105 | assert.Error(t, err) 106 | } 107 | 108 | func TestRun_BeginError(t *testing.T) { 109 | ctrl := gomock.NewController(t) 110 | defer ctrl.Finish() 111 | 112 | mockDB := NewMocktxStarter(ctrl) 113 | 114 | mockDB.EXPECT().BeginTx(gomock.Any(), gomock.Any()).Return(nil, errTest) 115 | 116 | runner, err := NewRunner(mockDB, pgx.TxOptions{}) 117 | assert.NoError(t, err) 118 | 119 | err = runner.Run(context.Background(), func(ctx context.Context) error { 120 | return nil 121 | }) 122 | assert.Error(t, err) 123 | } 124 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 2 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 4 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 5 | github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= 6 | github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= 7 | github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= 8 | github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= 9 | github.com/jackc/pgx/v5 v5.7.6 h1:rWQc5FwZSPX58r1OQmkuaNicxdmExaEz5A2DO2hUuTk= 10 | github.com/jackc/pgx/v5 v5.7.6/go.mod h1:aruU7o91Tc2q2cFp5h4uP3f6ztExVpyVv88Xl/8Vl8M= 11 | github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= 12 | github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= 13 | github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= 14 | github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= 15 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 16 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 17 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 18 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 19 | github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= 20 | github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= 21 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 22 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 23 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 24 | github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= 25 | github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 26 | go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= 27 | go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= 28 | golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04= 29 | golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0= 30 | golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= 31 | golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= 32 | golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k= 33 | golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= 34 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 35 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= 36 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 37 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 38 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 39 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 40 | -------------------------------------------------------------------------------- /query_test.go: -------------------------------------------------------------------------------- 1 | package pgxatomic 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "testing" 7 | 8 | "github.com/jackc/pgx/v5" 9 | "github.com/jackc/pgx/v5/pgconn" 10 | "github.com/stretchr/testify/assert" 11 | "go.uber.org/mock/gomock" 12 | ) 13 | 14 | func ctxWithTx(mockTx pgx.Tx) func(t *testing.T) context.Context { 15 | return func(t *testing.T) context.Context { 16 | t.Helper() 17 | return WithTx(context.Background(), mockTx) 18 | } 19 | } 20 | 21 | func ctxWithoutTx() func(t *testing.T) context.Context { 22 | return func(t *testing.T) context.Context { 23 | t.Helper() 24 | return context.Background() 25 | } 26 | } 27 | 28 | func TestQuery(t *testing.T) { 29 | ctrl := gomock.NewController(t) 30 | defer ctrl.Finish() 31 | 32 | mockTx := NewMockTx(ctrl) 33 | mockRows := NewMockRows(ctrl) 34 | queryErr := errors.New("query error") 35 | 36 | tests := []struct { 37 | name string 38 | setupCtx func(t *testing.T) context.Context 39 | setupMock func() 40 | sql string 41 | args []any 42 | want pgx.Rows 43 | wantErr error 44 | }{ 45 | { 46 | name: "tx from context", 47 | setupCtx: ctxWithTx(mockTx), 48 | setupMock: func() { 49 | mockTx.EXPECT(). 50 | Query(gomock.Any(), "SELECT * FROM users WHERE id = $1", 123). 51 | Return(mockRows, nil) 52 | }, 53 | sql: "SELECT * FROM users WHERE id = $1", 54 | args: []any{123}, 55 | want: mockRows, 56 | wantErr: nil, 57 | }, 58 | { 59 | name: "no tx in context", 60 | setupCtx: ctxWithoutTx(), 61 | setupMock: func() { 62 | mockTx.EXPECT(). 63 | Query(gomock.Any(), "SELECT * FROM products", gomock.Any()). 64 | Return(mockRows, nil) 65 | }, 66 | sql: "SELECT * FROM products", 67 | args: []any{}, 68 | want: mockRows, 69 | wantErr: nil, 70 | }, 71 | { 72 | name: "error from tx", 73 | setupCtx: ctxWithTx(mockTx), 74 | setupMock: func() { 75 | mockTx.EXPECT(). 76 | Query(gomock.Any(), "SELECT * FROM invalid", gomock.Any()). 77 | Return(nil, queryErr) 78 | }, 79 | sql: "SELECT * FROM invalid", 80 | args: []any{}, 81 | want: nil, 82 | wantErr: queryErr, 83 | }, 84 | { 85 | name: "tx from context multiple args", 86 | setupCtx: ctxWithTx(mockTx), 87 | setupMock: func() { 88 | mockTx.EXPECT(). 89 | Query(gomock.Any(), "SELECT * FROM users WHERE id = $1 AND status = $2", 42, "active"). 90 | Return(mockRows, nil) 91 | }, 92 | sql: "SELECT * FROM users WHERE id = $1 AND status = $2", 93 | args: []any{42, "active"}, 94 | want: mockRows, 95 | wantErr: nil, 96 | }, 97 | } 98 | 99 | for _, tt := range tests { 100 | t.Run(tt.name, func(t *testing.T) { 101 | ctx := tt.setupCtx(t) 102 | tt.setupMock() 103 | 104 | got, err := Query(ctx, mockTx, tt.sql, tt.args...) 105 | 106 | if tt.wantErr != nil { 107 | assert.Equal(t, tt.wantErr, err) 108 | } else { 109 | assert.NoError(t, err) 110 | } 111 | 112 | assert.Equal(t, tt.want, got) 113 | }) 114 | } 115 | } 116 | 117 | func TestExec(t *testing.T) { 118 | ctrl := gomock.NewController(t) 119 | defer ctrl.Finish() 120 | 121 | mockTx := NewMockTx(ctrl) 122 | execErr := errors.New("exec error") 123 | successTag := pgconn.NewCommandTag("UPDATE 5") 124 | 125 | tests := []struct { 126 | name string 127 | setupCtx func(t *testing.T) context.Context 128 | setupMock func() 129 | sql string 130 | args []any 131 | want pgconn.CommandTag 132 | wantErr error 133 | }{ 134 | { 135 | name: "tx from context", 136 | setupCtx: ctxWithTx(mockTx), 137 | setupMock: func() { 138 | mockTx.EXPECT(). 139 | Exec(gomock.Any(), "UPDATE users SET name = $1 WHERE id = $2", "John", 1). 140 | Return(successTag, nil) 141 | }, 142 | sql: "UPDATE users SET name = $1 WHERE id = $2", 143 | args: []any{"John", 1}, 144 | want: successTag, 145 | wantErr: nil, 146 | }, 147 | { 148 | name: "no tx in context", 149 | setupCtx: ctxWithoutTx(), 150 | setupMock: func() { 151 | mockTx.EXPECT(). 152 | Exec(gomock.Any(), "DELETE FROM users WHERE id = $1", 99). 153 | Return(pgconn.NewCommandTag("DELETE 1"), nil) 154 | }, 155 | sql: "DELETE FROM users WHERE id = $1", 156 | args: []any{99}, 157 | want: pgconn.NewCommandTag("DELETE 1"), 158 | wantErr: nil, 159 | }, 160 | { 161 | name: "error from tx", 162 | setupCtx: ctxWithTx(mockTx), 163 | setupMock: func() { 164 | mockTx.EXPECT(). 165 | Exec(gomock.Any(), "INVALID SQL", gomock.Any()). 166 | Return(pgconn.CommandTag{}, execErr) 167 | }, 168 | sql: "INVALID SQL", 169 | args: []any{}, 170 | want: pgconn.CommandTag{}, 171 | wantErr: execErr, 172 | }, 173 | { 174 | name: "tx from context insert", 175 | setupCtx: ctxWithTx(mockTx), 176 | setupMock: func() { 177 | mockTx.EXPECT(). 178 | Exec(gomock.Any(), "INSERT INTO users (name, email) VALUES ($1, $2)", "Alice", "alice@example.com"). 179 | Return(pgconn.NewCommandTag("INSERT 1"), nil) 180 | }, 181 | sql: "INSERT INTO users (name, email) VALUES ($1, $2)", 182 | args: []any{"Alice", "alice@example.com"}, 183 | want: pgconn.NewCommandTag("INSERT 1"), 184 | wantErr: nil, 185 | }, 186 | } 187 | 188 | for _, tt := range tests { 189 | t.Run(tt.name, func(t *testing.T) { 190 | ctx := tt.setupCtx(t) 191 | tt.setupMock() 192 | 193 | got, err := Exec(ctx, mockTx, tt.sql, tt.args...) 194 | 195 | if tt.wantErr != nil { 196 | assert.Equal(t, tt.wantErr, err) 197 | } else { 198 | assert.NoError(t, err) 199 | } 200 | 201 | assert.Equal(t, tt.want, got) 202 | }) 203 | } 204 | } 205 | 206 | func TestQueryRow(t *testing.T) { 207 | ctrl := gomock.NewController(t) 208 | defer ctrl.Finish() 209 | 210 | mockTx := NewMockTx(ctrl) 211 | mockRow := NewMockRow(ctrl) 212 | 213 | tests := []struct { 214 | name string 215 | setupCtx func(t *testing.T) context.Context 216 | setupMock func() 217 | sql string 218 | args []any 219 | want pgx.Row 220 | }{ 221 | { 222 | name: "tx from context", 223 | setupCtx: ctxWithTx(mockTx), 224 | setupMock: func() { 225 | mockTx.EXPECT(). 226 | QueryRow(gomock.Any(), "SELECT * FROM users WHERE id = $1", 42). 227 | Return(mockRow) 228 | }, 229 | sql: "SELECT * FROM users WHERE id = $1", 230 | args: []any{42}, 231 | want: mockRow, 232 | }, 233 | { 234 | name: "no tx in context", 235 | setupCtx: ctxWithoutTx(), 236 | setupMock: func() { 237 | mockTx.EXPECT(). 238 | QueryRow(gomock.Any(), "SELECT COUNT(*) FROM products", gomock.Any()). 239 | Return(mockRow) 240 | }, 241 | sql: "SELECT COUNT(*) FROM products", 242 | args: []any{}, 243 | want: mockRow, 244 | }, 245 | { 246 | name: "tx from context multiple args", 247 | setupCtx: ctxWithTx(mockTx), 248 | setupMock: func() { 249 | mockTx.EXPECT(). 250 | QueryRow(gomock.Any(), "SELECT * FROM users WHERE email = $1 AND status = $2", "test@example.com", "active"). 251 | Return(mockRow) 252 | }, 253 | sql: "SELECT * FROM users WHERE email = $1 AND status = $2", 254 | args: []any{"test@example.com", "active"}, 255 | want: mockRow, 256 | }, 257 | { 258 | name: "tx from context aggregate", 259 | setupCtx: ctxWithTx(mockTx), 260 | setupMock: func() { 261 | mockTx.EXPECT(). 262 | QueryRow(gomock.Any(), "SELECT MAX(id) FROM users", gomock.Any()). 263 | Return(mockRow) 264 | }, 265 | sql: "SELECT MAX(id) FROM users", 266 | args: []any{}, 267 | want: mockRow, 268 | }, 269 | } 270 | 271 | for _, tt := range tests { 272 | t.Run(tt.name, func(t *testing.T) { 273 | ctx := tt.setupCtx(t) 274 | tt.setupMock() 275 | 276 | got := QueryRow(ctx, mockTx, tt.sql, tt.args...) 277 | 278 | assert.Equal(t, tt.want, got) 279 | }) 280 | } 281 | } 282 | -------------------------------------------------------------------------------- /mocks_test.go: -------------------------------------------------------------------------------- 1 | // Code generated by MockGen. DO NOT EDIT. 2 | // Source: github.com/jackc/pgx/v5 (interfaces: Rows,Row,Tx) 3 | // 4 | // Generated by this command: 5 | // 6 | // mockgen -package pgxatomic -destination mocks_test.go github.com/jackc/pgx/v5 Rows,Row,Tx 7 | // 8 | 9 | // Package pgxatomic is a generated GoMock package. 10 | package pgxatomic 11 | 12 | import ( 13 | context "context" 14 | reflect "reflect" 15 | 16 | pgx "github.com/jackc/pgx/v5" 17 | pgconn "github.com/jackc/pgx/v5/pgconn" 18 | gomock "go.uber.org/mock/gomock" 19 | ) 20 | 21 | // MockRows is a mock of Rows interface. 22 | type MockRows struct { 23 | ctrl *gomock.Controller 24 | recorder *MockRowsMockRecorder 25 | isgomock struct{} 26 | } 27 | 28 | // MockRowsMockRecorder is the mock recorder for MockRows. 29 | type MockRowsMockRecorder struct { 30 | mock *MockRows 31 | } 32 | 33 | // NewMockRows creates a new mock instance. 34 | func NewMockRows(ctrl *gomock.Controller) *MockRows { 35 | mock := &MockRows{ctrl: ctrl} 36 | mock.recorder = &MockRowsMockRecorder{mock} 37 | return mock 38 | } 39 | 40 | // EXPECT returns an object that allows the caller to indicate expected use. 41 | func (m *MockRows) EXPECT() *MockRowsMockRecorder { 42 | return m.recorder 43 | } 44 | 45 | // Close mocks base method. 46 | func (m *MockRows) Close() { 47 | m.ctrl.T.Helper() 48 | m.ctrl.Call(m, "Close") 49 | } 50 | 51 | // Close indicates an expected call of Close. 52 | func (mr *MockRowsMockRecorder) Close() *gomock.Call { 53 | mr.mock.ctrl.T.Helper() 54 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockRows)(nil).Close)) 55 | } 56 | 57 | // CommandTag mocks base method. 58 | func (m *MockRows) CommandTag() pgconn.CommandTag { 59 | m.ctrl.T.Helper() 60 | ret := m.ctrl.Call(m, "CommandTag") 61 | ret0, _ := ret[0].(pgconn.CommandTag) 62 | return ret0 63 | } 64 | 65 | // CommandTag indicates an expected call of CommandTag. 66 | func (mr *MockRowsMockRecorder) CommandTag() *gomock.Call { 67 | mr.mock.ctrl.T.Helper() 68 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CommandTag", reflect.TypeOf((*MockRows)(nil).CommandTag)) 69 | } 70 | 71 | // Conn mocks base method. 72 | func (m *MockRows) Conn() *pgx.Conn { 73 | m.ctrl.T.Helper() 74 | ret := m.ctrl.Call(m, "Conn") 75 | ret0, _ := ret[0].(*pgx.Conn) 76 | return ret0 77 | } 78 | 79 | // Conn indicates an expected call of Conn. 80 | func (mr *MockRowsMockRecorder) Conn() *gomock.Call { 81 | mr.mock.ctrl.T.Helper() 82 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Conn", reflect.TypeOf((*MockRows)(nil).Conn)) 83 | } 84 | 85 | // Err mocks base method. 86 | func (m *MockRows) Err() error { 87 | m.ctrl.T.Helper() 88 | ret := m.ctrl.Call(m, "Err") 89 | ret0, _ := ret[0].(error) 90 | return ret0 91 | } 92 | 93 | // Err indicates an expected call of Err. 94 | func (mr *MockRowsMockRecorder) Err() *gomock.Call { 95 | mr.mock.ctrl.T.Helper() 96 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Err", reflect.TypeOf((*MockRows)(nil).Err)) 97 | } 98 | 99 | // FieldDescriptions mocks base method. 100 | func (m *MockRows) FieldDescriptions() []pgconn.FieldDescription { 101 | m.ctrl.T.Helper() 102 | ret := m.ctrl.Call(m, "FieldDescriptions") 103 | ret0, _ := ret[0].([]pgconn.FieldDescription) 104 | return ret0 105 | } 106 | 107 | // FieldDescriptions indicates an expected call of FieldDescriptions. 108 | func (mr *MockRowsMockRecorder) FieldDescriptions() *gomock.Call { 109 | mr.mock.ctrl.T.Helper() 110 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FieldDescriptions", reflect.TypeOf((*MockRows)(nil).FieldDescriptions)) 111 | } 112 | 113 | // Next mocks base method. 114 | func (m *MockRows) Next() bool { 115 | m.ctrl.T.Helper() 116 | ret := m.ctrl.Call(m, "Next") 117 | ret0, _ := ret[0].(bool) 118 | return ret0 119 | } 120 | 121 | // Next indicates an expected call of Next. 122 | func (mr *MockRowsMockRecorder) Next() *gomock.Call { 123 | mr.mock.ctrl.T.Helper() 124 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Next", reflect.TypeOf((*MockRows)(nil).Next)) 125 | } 126 | 127 | // RawValues mocks base method. 128 | func (m *MockRows) RawValues() [][]byte { 129 | m.ctrl.T.Helper() 130 | ret := m.ctrl.Call(m, "RawValues") 131 | ret0, _ := ret[0].([][]byte) 132 | return ret0 133 | } 134 | 135 | // RawValues indicates an expected call of RawValues. 136 | func (mr *MockRowsMockRecorder) RawValues() *gomock.Call { 137 | mr.mock.ctrl.T.Helper() 138 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RawValues", reflect.TypeOf((*MockRows)(nil).RawValues)) 139 | } 140 | 141 | // Scan mocks base method. 142 | func (m *MockRows) Scan(dest ...any) error { 143 | m.ctrl.T.Helper() 144 | varargs := []any{} 145 | for _, a := range dest { 146 | varargs = append(varargs, a) 147 | } 148 | ret := m.ctrl.Call(m, "Scan", varargs...) 149 | ret0, _ := ret[0].(error) 150 | return ret0 151 | } 152 | 153 | // Scan indicates an expected call of Scan. 154 | func (mr *MockRowsMockRecorder) Scan(dest ...any) *gomock.Call { 155 | mr.mock.ctrl.T.Helper() 156 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Scan", reflect.TypeOf((*MockRows)(nil).Scan), dest...) 157 | } 158 | 159 | // Values mocks base method. 160 | func (m *MockRows) Values() ([]any, error) { 161 | m.ctrl.T.Helper() 162 | ret := m.ctrl.Call(m, "Values") 163 | ret0, _ := ret[0].([]any) 164 | ret1, _ := ret[1].(error) 165 | return ret0, ret1 166 | } 167 | 168 | // Values indicates an expected call of Values. 169 | func (mr *MockRowsMockRecorder) Values() *gomock.Call { 170 | mr.mock.ctrl.T.Helper() 171 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Values", reflect.TypeOf((*MockRows)(nil).Values)) 172 | } 173 | 174 | // MockRow is a mock of Row interface. 175 | type MockRow struct { 176 | ctrl *gomock.Controller 177 | recorder *MockRowMockRecorder 178 | isgomock struct{} 179 | } 180 | 181 | // MockRowMockRecorder is the mock recorder for MockRow. 182 | type MockRowMockRecorder struct { 183 | mock *MockRow 184 | } 185 | 186 | // NewMockRow creates a new mock instance. 187 | func NewMockRow(ctrl *gomock.Controller) *MockRow { 188 | mock := &MockRow{ctrl: ctrl} 189 | mock.recorder = &MockRowMockRecorder{mock} 190 | return mock 191 | } 192 | 193 | // EXPECT returns an object that allows the caller to indicate expected use. 194 | func (m *MockRow) EXPECT() *MockRowMockRecorder { 195 | return m.recorder 196 | } 197 | 198 | // Scan mocks base method. 199 | func (m *MockRow) Scan(dest ...any) error { 200 | m.ctrl.T.Helper() 201 | varargs := []any{} 202 | for _, a := range dest { 203 | varargs = append(varargs, a) 204 | } 205 | ret := m.ctrl.Call(m, "Scan", varargs...) 206 | ret0, _ := ret[0].(error) 207 | return ret0 208 | } 209 | 210 | // Scan indicates an expected call of Scan. 211 | func (mr *MockRowMockRecorder) Scan(dest ...any) *gomock.Call { 212 | mr.mock.ctrl.T.Helper() 213 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Scan", reflect.TypeOf((*MockRow)(nil).Scan), dest...) 214 | } 215 | 216 | // MockTx is a mock of Tx interface. 217 | type MockTx struct { 218 | ctrl *gomock.Controller 219 | recorder *MockTxMockRecorder 220 | isgomock struct{} 221 | } 222 | 223 | // MockTxMockRecorder is the mock recorder for MockTx. 224 | type MockTxMockRecorder struct { 225 | mock *MockTx 226 | } 227 | 228 | // NewMockTx creates a new mock instance. 229 | func NewMockTx(ctrl *gomock.Controller) *MockTx { 230 | mock := &MockTx{ctrl: ctrl} 231 | mock.recorder = &MockTxMockRecorder{mock} 232 | return mock 233 | } 234 | 235 | // EXPECT returns an object that allows the caller to indicate expected use. 236 | func (m *MockTx) EXPECT() *MockTxMockRecorder { 237 | return m.recorder 238 | } 239 | 240 | // Begin mocks base method. 241 | func (m *MockTx) Begin(ctx context.Context) (pgx.Tx, error) { 242 | m.ctrl.T.Helper() 243 | ret := m.ctrl.Call(m, "Begin", ctx) 244 | ret0, _ := ret[0].(pgx.Tx) 245 | ret1, _ := ret[1].(error) 246 | return ret0, ret1 247 | } 248 | 249 | // Begin indicates an expected call of Begin. 250 | func (mr *MockTxMockRecorder) Begin(ctx any) *gomock.Call { 251 | mr.mock.ctrl.T.Helper() 252 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Begin", reflect.TypeOf((*MockTx)(nil).Begin), ctx) 253 | } 254 | 255 | // Commit mocks base method. 256 | func (m *MockTx) Commit(ctx context.Context) error { 257 | m.ctrl.T.Helper() 258 | ret := m.ctrl.Call(m, "Commit", ctx) 259 | ret0, _ := ret[0].(error) 260 | return ret0 261 | } 262 | 263 | // Commit indicates an expected call of Commit. 264 | func (mr *MockTxMockRecorder) Commit(ctx any) *gomock.Call { 265 | mr.mock.ctrl.T.Helper() 266 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Commit", reflect.TypeOf((*MockTx)(nil).Commit), ctx) 267 | } 268 | 269 | // Conn mocks base method. 270 | func (m *MockTx) Conn() *pgx.Conn { 271 | m.ctrl.T.Helper() 272 | ret := m.ctrl.Call(m, "Conn") 273 | ret0, _ := ret[0].(*pgx.Conn) 274 | return ret0 275 | } 276 | 277 | // Conn indicates an expected call of Conn. 278 | func (mr *MockTxMockRecorder) Conn() *gomock.Call { 279 | mr.mock.ctrl.T.Helper() 280 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Conn", reflect.TypeOf((*MockTx)(nil).Conn)) 281 | } 282 | 283 | // CopyFrom mocks base method. 284 | func (m *MockTx) CopyFrom(ctx context.Context, tableName pgx.Identifier, columnNames []string, rowSrc pgx.CopyFromSource) (int64, error) { 285 | m.ctrl.T.Helper() 286 | ret := m.ctrl.Call(m, "CopyFrom", ctx, tableName, columnNames, rowSrc) 287 | ret0, _ := ret[0].(int64) 288 | ret1, _ := ret[1].(error) 289 | return ret0, ret1 290 | } 291 | 292 | // CopyFrom indicates an expected call of CopyFrom. 293 | func (mr *MockTxMockRecorder) CopyFrom(ctx, tableName, columnNames, rowSrc any) *gomock.Call { 294 | mr.mock.ctrl.T.Helper() 295 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CopyFrom", reflect.TypeOf((*MockTx)(nil).CopyFrom), ctx, tableName, columnNames, rowSrc) 296 | } 297 | 298 | // Exec mocks base method. 299 | func (m *MockTx) Exec(ctx context.Context, sql string, arguments ...any) (pgconn.CommandTag, error) { 300 | m.ctrl.T.Helper() 301 | varargs := []any{ctx, sql} 302 | for _, a := range arguments { 303 | varargs = append(varargs, a) 304 | } 305 | ret := m.ctrl.Call(m, "Exec", varargs...) 306 | ret0, _ := ret[0].(pgconn.CommandTag) 307 | ret1, _ := ret[1].(error) 308 | return ret0, ret1 309 | } 310 | 311 | // Exec indicates an expected call of Exec. 312 | func (mr *MockTxMockRecorder) Exec(ctx, sql any, arguments ...any) *gomock.Call { 313 | mr.mock.ctrl.T.Helper() 314 | varargs := append([]any{ctx, sql}, arguments...) 315 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Exec", reflect.TypeOf((*MockTx)(nil).Exec), varargs...) 316 | } 317 | 318 | // LargeObjects mocks base method. 319 | func (m *MockTx) LargeObjects() pgx.LargeObjects { 320 | m.ctrl.T.Helper() 321 | ret := m.ctrl.Call(m, "LargeObjects") 322 | ret0, _ := ret[0].(pgx.LargeObjects) 323 | return ret0 324 | } 325 | 326 | // LargeObjects indicates an expected call of LargeObjects. 327 | func (mr *MockTxMockRecorder) LargeObjects() *gomock.Call { 328 | mr.mock.ctrl.T.Helper() 329 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LargeObjects", reflect.TypeOf((*MockTx)(nil).LargeObjects)) 330 | } 331 | 332 | // Prepare mocks base method. 333 | func (m *MockTx) Prepare(ctx context.Context, name, sql string) (*pgconn.StatementDescription, error) { 334 | m.ctrl.T.Helper() 335 | ret := m.ctrl.Call(m, "Prepare", ctx, name, sql) 336 | ret0, _ := ret[0].(*pgconn.StatementDescription) 337 | ret1, _ := ret[1].(error) 338 | return ret0, ret1 339 | } 340 | 341 | // Prepare indicates an expected call of Prepare. 342 | func (mr *MockTxMockRecorder) Prepare(ctx, name, sql any) *gomock.Call { 343 | mr.mock.ctrl.T.Helper() 344 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Prepare", reflect.TypeOf((*MockTx)(nil).Prepare), ctx, name, sql) 345 | } 346 | 347 | // Query mocks base method. 348 | func (m *MockTx) Query(ctx context.Context, sql string, args ...any) (pgx.Rows, error) { 349 | m.ctrl.T.Helper() 350 | varargs := []any{ctx, sql} 351 | for _, a := range args { 352 | varargs = append(varargs, a) 353 | } 354 | ret := m.ctrl.Call(m, "Query", varargs...) 355 | ret0, _ := ret[0].(pgx.Rows) 356 | ret1, _ := ret[1].(error) 357 | return ret0, ret1 358 | } 359 | 360 | // Query indicates an expected call of Query. 361 | func (mr *MockTxMockRecorder) Query(ctx, sql any, args ...any) *gomock.Call { 362 | mr.mock.ctrl.T.Helper() 363 | varargs := append([]any{ctx, sql}, args...) 364 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Query", reflect.TypeOf((*MockTx)(nil).Query), varargs...) 365 | } 366 | 367 | // QueryRow mocks base method. 368 | func (m *MockTx) QueryRow(ctx context.Context, sql string, args ...any) pgx.Row { 369 | m.ctrl.T.Helper() 370 | varargs := []any{ctx, sql} 371 | for _, a := range args { 372 | varargs = append(varargs, a) 373 | } 374 | ret := m.ctrl.Call(m, "QueryRow", varargs...) 375 | ret0, _ := ret[0].(pgx.Row) 376 | return ret0 377 | } 378 | 379 | // QueryRow indicates an expected call of QueryRow. 380 | func (mr *MockTxMockRecorder) QueryRow(ctx, sql any, args ...any) *gomock.Call { 381 | mr.mock.ctrl.T.Helper() 382 | varargs := append([]any{ctx, sql}, args...) 383 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "QueryRow", reflect.TypeOf((*MockTx)(nil).QueryRow), varargs...) 384 | } 385 | 386 | // Rollback mocks base method. 387 | func (m *MockTx) Rollback(ctx context.Context) error { 388 | m.ctrl.T.Helper() 389 | ret := m.ctrl.Call(m, "Rollback", ctx) 390 | ret0, _ := ret[0].(error) 391 | return ret0 392 | } 393 | 394 | // Rollback indicates an expected call of Rollback. 395 | func (mr *MockTxMockRecorder) Rollback(ctx any) *gomock.Call { 396 | mr.mock.ctrl.T.Helper() 397 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Rollback", reflect.TypeOf((*MockTx)(nil).Rollback), ctx) 398 | } 399 | 400 | // SendBatch mocks base method. 401 | func (m *MockTx) SendBatch(ctx context.Context, b *pgx.Batch) pgx.BatchResults { 402 | m.ctrl.T.Helper() 403 | ret := m.ctrl.Call(m, "SendBatch", ctx, b) 404 | ret0, _ := ret[0].(pgx.BatchResults) 405 | return ret0 406 | } 407 | 408 | // SendBatch indicates an expected call of SendBatch. 409 | func (mr *MockTxMockRecorder) SendBatch(ctx, b any) *gomock.Call { 410 | mr.mock.ctrl.T.Helper() 411 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendBatch", reflect.TypeOf((*MockTx)(nil).SendBatch), ctx, b) 412 | } 413 | --------------------------------------------------------------------------------