From f753be22ba5a98c60ff1bf2ec87451ee78405440 Mon Sep 17 00:00:00 2001 From: Dmitrii Aleksandrov Date: Sun, 20 Apr 2025 00:02:28 +0300 Subject: [PATCH 1/2] src: executor: adapters: add pgx v5 adapter Signed-off-by: Dmitrii Aleksandrov --- README.md | 19 ++++++++--- executor/adapters/pgx5/pool.go | 54 +++++++++++++++++++++++++++++ executor/adapters/pgx5/result.go | 11 ++++++ executor/adapters/pgx5/rows.go | 22 ++++++++++++ executor/adapters/pgx5/tx.go | 58 ++++++++++++++++++++++++++++++++ go.mod | 9 +++-- go.sum | 11 ++++-- 7 files changed, 173 insertions(+), 11 deletions(-) create mode 100644 executor/adapters/pgx5/pool.go create mode 100644 executor/adapters/pgx5/result.go create mode 100644 executor/adapters/pgx5/rows.go create mode 100644 executor/adapters/pgx5/tx.go diff --git a/README.md b/README.md index 8d7de4d..4089ad8 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ This project under active development. ## Features Essentially, **GERPO** is a helper for building SQL queries and mapping results to Go structs. - **Data Sources (executor adapters)**: - - pgx pool v4 + - pgx pool v4/v5 - any database/sql driver - any other: you can add dbWrapper for any other database library, by implementing simple wrapper - executor.DBWrapper. - **Repository configuration**: @@ -63,19 +63,28 @@ import ( "github.com/insei/gerpo" "github.com/insei/gerpo/executor/adapters/databasesql" "github.com/insei/gerpo/executor/adapters/pgx4" + "github.com/insei/gerpo/executor/adapters/pgx5" + "github.com/insei/gerpo/executor/adapters/placeholder" "github.com/jackc/pgx/v4/pgxpool" ) func main() { - // for database/sql + // for database/sql postgres variant // "github.com/insei/gerpo/executor/adapters/databasesql" var db *sql.DB - dbWrap := databasesql.NewAdapter(db) + // for postgres change placeholder to dollar, by default placeholder is Question + phOption := databasesql.WithPlaceholder(placeholder.Dollar) + dbWrap := databasesql.NewAdapter(db, phOption) // for pgx4 pool // "github.com/insei/gerpo/executor/adapters/pgx4" - var pool *pgxpool.Pool - dbWrap := pgx4.NewPoolAdapter(pool) + var poolv4 *pgxpool.Pool + dbWrap = pgx4.NewPoolAdapter(poolv4) + + // for pgx5 pool + // "github.com/insei/gerpo/executor/adapters/pgx5" + var poolv5 *pgxpool.Pool + dbWrap = pgx5.NewPoolAdapter(poolv5) repo, err := gerpo.NewBuilder[ModelType]().DB(dbWrap) // ... diff --git a/executor/adapters/pgx5/pool.go b/executor/adapters/pgx5/pool.go new file mode 100644 index 0000000..fa0a70e --- /dev/null +++ b/executor/adapters/pgx5/pool.go @@ -0,0 +1,54 @@ +package pgx4 + +import ( + "context" + "fmt" + + "github.com/insei/gerpo/executor/adapters/placeholder" + "github.com/insei/gerpo/executor/types" + "github.com/jackc/pgx/v5" + "github.com/jackc/pgx/v5/pgxpool" +) + +type poolWrap struct { + pool *pgxpool.Pool +} + +func (p *poolWrap) ExecContext(ctx context.Context, query string, args ...any) (types.Result, error) { + sql, err := placeholder.Dollar.ReplacePlaceholders(query) + if err != nil { + return nil, fmt.Errorf("failed to replace placeholders: %w", err) + } + res, err := p.pool.Exec(ctx, sql, args...) + if err != nil { + return nil, err + } + return &resultWrap{res: res}, nil +} + +func (p *poolWrap) QueryContext(ctx context.Context, query string, args ...any) (types.Rows, error) { + sql, err := placeholder.Dollar.ReplacePlaceholders(query) + if err != nil { + return nil, fmt.Errorf("failed to replace placeholders: %w", err) + } + rows, err := p.pool.Query(ctx, sql, args...) + if err != nil { + return nil, err + } + return &rowsWrap{rows: rows}, nil +} + +func (p *poolWrap) BeginTx(ctx context.Context) (types.Tx, error) { + tx, err := p.pool.BeginTx(ctx, pgx.TxOptions{}) + if err != nil { + return nil, err + } + return &txWrap{ + rollbackUnlessCommittedNeeded: true, + tx: tx, + }, err +} + +func NewPoolAdapter(pool *pgxpool.Pool) types.DBAdapter { + return &poolWrap{pool} +} diff --git a/executor/adapters/pgx5/result.go b/executor/adapters/pgx5/result.go new file mode 100644 index 0000000..64a547c --- /dev/null +++ b/executor/adapters/pgx5/result.go @@ -0,0 +1,11 @@ +package pgx4 + +import "github.com/jackc/pgx/v5/pgconn" + +type resultWrap struct { + res pgconn.CommandTag +} + +func (e *resultWrap) RowsAffected() (int64, error) { + return e.res.RowsAffected(), nil +} diff --git a/executor/adapters/pgx5/rows.go b/executor/adapters/pgx5/rows.go new file mode 100644 index 0000000..4ed1c43 --- /dev/null +++ b/executor/adapters/pgx5/rows.go @@ -0,0 +1,22 @@ +package pgx4 + +import ( + "github.com/jackc/pgx/v5" +) + +type rowsWrap struct { + rows pgx.Rows +} + +func (r *rowsWrap) Next() bool { + return r.rows.Next() +} + +func (r *rowsWrap) Scan(dest ...interface{}) error { + return r.rows.Scan(dest...) +} + +func (r *rowsWrap) Close() error { + r.rows.Close() + return nil +} diff --git a/executor/adapters/pgx5/tx.go b/executor/adapters/pgx5/tx.go new file mode 100644 index 0000000..677ebeb --- /dev/null +++ b/executor/adapters/pgx5/tx.go @@ -0,0 +1,58 @@ +package pgx4 + +import ( + "context" + "fmt" + + "github.com/insei/gerpo/executor/adapters/placeholder" + extypes "github.com/insei/gerpo/executor/types" + "github.com/jackc/pgx/v5" +) + +type txWrap struct { + commited bool + rollbackUnlessCommittedNeeded bool + tx pgx.Tx +} + +func (t txWrap) Rollback() error { + return t.tx.Rollback(context.Background()) +} + +func (t txWrap) Commit() error { + return t.tx.Commit(context.Background()) +} + +func (t txWrap) RollbackUnlessCommitted() error { + if !t.commited && t.rollbackUnlessCommittedNeeded { + err := t.Rollback() + if err != nil { + return err + } + } + return nil +} + +func (t txWrap) ExecContext(ctx context.Context, query string, args ...any) (extypes.Result, error) { + sql, err := placeholder.Dollar.ReplacePlaceholders(query) + if err != nil { + return nil, fmt.Errorf("failed to replace placeholders: %w", err) + } + res, err := t.tx.Exec(ctx, sql, args...) + if err != nil { + return nil, err + } + return &resultWrap{res: res}, nil +} + +func (t txWrap) QueryContext(ctx context.Context, query string, args ...any) (extypes.Rows, error) { + sql, err := placeholder.Dollar.ReplacePlaceholders(query) + if err != nil { + return nil, fmt.Errorf("failed to replace placeholders: %w", err) + } + rows, err := t.tx.Query(ctx, sql, args...) + if err != nil { + return nil, err + } + return &rowsWrap{rows: rows}, nil +} diff --git a/go.mod b/go.mod index 79c6388..143b9fb 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( github.com/insei/fmap/v3 v3.1.2 github.com/jackc/pgconn v1.14.3 github.com/jackc/pgx/v4 v4.18.3 + github.com/jackc/pgx/v5 v5.7.4 github.com/stretchr/testify v1.10.0 ) @@ -17,14 +18,16 @@ require ( github.com/jackc/pgio v1.0.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgproto3/v2 v2.3.3 // indirect - github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect + github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect github.com/jackc/pgtype v1.14.0 // indirect github.com/jackc/puddle v1.3.0 // indirect - github.com/kr/pretty v0.3.0 // indirect + github.com/jackc/puddle/v2 v2.2.2 // indirect + github.com/kr/text v0.2.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/rogpeppe/go-internal v1.6.1 // indirect github.com/stretchr/objx v0.5.2 // indirect golang.org/x/crypto v0.31.0 // indirect + golang.org/x/sync v0.11.0 // indirect golang.org/x/text v0.22.0 // indirect - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index e0eb6e0..5de3ec8 100644 --- a/go.sum +++ b/go.sum @@ -51,8 +51,8 @@ github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwX github.com/jackc/pgproto3/v2 v2.3.3 h1:1HLSx5H+tXR9pW3in3zaztoEwQYRC9SQaYUHjTSUOag= github.com/jackc/pgproto3/v2 v2.3.3/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= -github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= -github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= @@ -65,17 +65,20 @@ github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQ github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= github.com/jackc/pgx/v4 v4.18.3 h1:dE2/TrEsGX3RBprb3qryqSV9Y60iZN1C6i8IrmW9/BA= github.com/jackc/pgx/v4 v4.18.3/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx2CkVw= +github.com/jackc/pgx/v5 v5.7.4 h1:9wKznZrhWa2QiHL+NjTSPP6yjl3451BX3imWDnokYlg= +github.com/jackc/pgx/v5 v5.7.4/go.mod h1:ncY89UGWxg82EykZUwSpUKEfccBGGYq1xjrOpsbsfGQ= github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.3.0 h1:eHK/5clGOatcjX3oWGBO/MpxpbHzSwud5EWTSCI+MX0= github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= +github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46/go.mod h1:yyMNCyc/Ib3bDTKd379tNMpB/7/H5TjM2Y9QJ5THLbE= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -153,6 +156,8 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= +golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= From 497588ff511ace52c54e602ee7698c4e4fe19e2e Mon Sep 17 00:00:00 2001 From: Dmitrii Aleksandrov Date: Sun, 20 Apr 2025 00:05:26 +0300 Subject: [PATCH 2/2] ci: exclude adapters from code coverage tests result Signed-off-by: Dmitrii Aleksandrov --- .github/workflows/go.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 073eebf..7edb4b4 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -44,7 +44,9 @@ jobs: run: go build -v ./... - name: Test - run: go test -v -coverprofile=coverage.txt -covermode=atomic ./... + run: go test -v -coverprofile=coverage.txt.tmp -covermode=atomic ./... + - name: Exclude adapters + run: cat coverage.txt.tmp | grep -v "/adapters/" > coverage.txt && rm coverage.txt.tmp - uses: codecov/codecov-action@v4 with: token: ${{ secrets.CODECOV_TOKEN }} \ No newline at end of file