Select and Tx functions added
This commit is contained in:
parent
2e8cd09152
commit
a96c317795
8
.idea/.gitignore
vendored
Normal file
8
.idea/.gitignore
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
8
.idea/modules.xml
Normal file
8
.idea/modules.xml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/nettools.iml" filepath="$PROJECT_DIR$/.idea/nettools.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
9
.idea/nettools.iml
Normal file
9
.idea/nettools.iml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="WEB_MODULE" version="4">
|
||||||
|
<component name="Go" enabled="true" />
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$" />
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
6
.idea/vcs.xml
Normal file
6
.idea/vcs.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
8
go.mod
8
go.mod
@ -1,3 +1,11 @@
|
|||||||
module nettools
|
module nettools
|
||||||
|
|
||||||
go 1.23.1
|
go 1.23.1
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
||||||
|
github.com/jackc/pgx/v5 v5.7.1 // indirect
|
||||||
|
golang.org/x/crypto v0.27.0 // indirect
|
||||||
|
golang.org/x/text v0.18.0 // indirect
|
||||||
|
)
|
||||||
|
17
go.sum
Normal file
17
go.sum
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||||
|
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||||
|
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/pgx/v5 v5.7.1 h1:x7SYsPBYDkHDksogeSmZZ5xzThcTgRz++I5E+ePFUcs=
|
||||||
|
github.com/jackc/pgx/v5 v5.7.1/go.mod h1:e7O26IywZZ+naJtWWos6i6fvWK+29etgITqrqHLfoZA=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
|
||||||
|
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
|
||||||
|
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
|
||||||
|
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
85
pg/pg.go
Normal file
85
pg/pg.go
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
package pgUtils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"github.com/jackc/pgx/v5"
|
||||||
|
"github.com/jackc/pgx/v5/pgxpool"
|
||||||
|
reflectUtils "nettools/reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrEntityNotFound = errors.New("entity not found")
|
||||||
|
ErrTooManyRows = errors.New("too many rows")
|
||||||
|
ErrEntityAlreadyExists = errors.New("entity already exists")
|
||||||
|
ErrNoDstFields = errors.New("no destination fields")
|
||||||
|
)
|
||||||
|
|
||||||
|
type PgxQuerier interface {
|
||||||
|
Query(ctx context.Context, sql string, args ...any) (pgx.Rows, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Select executes query on a provided querier and tries to parse db response into antit
|
||||||
|
// Works only with objects
|
||||||
|
//
|
||||||
|
// Usage:
|
||||||
|
//
|
||||||
|
// type User struct {
|
||||||
|
// id int
|
||||||
|
// name string
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// db := pgx.Connect(context.Background(), "<url>")
|
||||||
|
// users, err := pgUtils.Select[User](context.Background(), db, "SELECT * FROM users")
|
||||||
|
func Select[T any](ctx context.Context, db PgxQuerier, query string, args ...any) (out []*T, err error) {
|
||||||
|
out = []*T{}
|
||||||
|
rows, err := db.Query(ctx, query, args)
|
||||||
|
if err != nil {
|
||||||
|
switch {
|
||||||
|
case errors.Is(err, pgx.ErrNoRows):
|
||||||
|
err = ErrEntityNotFound
|
||||||
|
} // TODO: extend cases
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get column names
|
||||||
|
columns := make([]string, len(rows.FieldDescriptions()))
|
||||||
|
for i, fd := range rows.FieldDescriptions() {
|
||||||
|
columns[i] = fd.Name
|
||||||
|
}
|
||||||
|
itemFieldPtrs := make([]interface{}, len(columns))
|
||||||
|
|
||||||
|
defer rows.Close()
|
||||||
|
for rows.Next() {
|
||||||
|
item := new(T)
|
||||||
|
dstItemPtrsMap, err := reflectUtils.GetEntityPtrs(item, "db")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for i, columnName := range columns {
|
||||||
|
itemFieldPtrs[i] = dstItemPtrsMap[columnName]
|
||||||
|
}
|
||||||
|
if len(itemFieldPtrs) == 0 {
|
||||||
|
return nil, ErrNoDstFields
|
||||||
|
}
|
||||||
|
if err = rows.Scan(itemFieldPtrs...); err != nil {
|
||||||
|
return out, err
|
||||||
|
}
|
||||||
|
out = append(out, item)
|
||||||
|
}
|
||||||
|
return out, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tx creates new transaction. Cancels it if returned not nil err
|
||||||
|
func Tx(ctx context.Context, db *pgxpool.Pool, exec func(ctx context.Context, tx pgx.Tx) error) error {
|
||||||
|
tx, err := db.Begin(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = exec(ctx, tx)
|
||||||
|
if err != nil {
|
||||||
|
_ = tx.Rollback(ctx)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return tx.Commit(ctx)
|
||||||
|
}
|
28
reflect/extract_by_tags.go
Normal file
28
reflect/extract_by_tags.go
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
package reflectUtils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ErrNilDst = errors.New("destination is nil")
|
||||||
|
|
||||||
|
func GetEntityPtrs(dstItem any, tag string) (dstItemPtrsMap map[string]any, err error) {
|
||||||
|
if dstItem == nil {
|
||||||
|
return nil, errors.Join(errors.New("error parsing destination"), ErrNilDst)
|
||||||
|
}
|
||||||
|
v := reflect.ValueOf(dstItem).Elem()
|
||||||
|
t := v.Type()
|
||||||
|
|
||||||
|
dstItemPtrsMap = make(map[string]any)
|
||||||
|
for i := 0; i < t.NumField(); i++ {
|
||||||
|
field := t.Field(i)
|
||||||
|
dbTag := field.Tag.Get(tag)
|
||||||
|
if dbTag != "" {
|
||||||
|
valueField := v.Field(i).Addr().Interface()
|
||||||
|
dstItemPtrsMap[dbTag] = &valueField
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dstItemPtrsMap, nil
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user