diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..13566b8
--- /dev/null
+++ b/.idea/.gitignore
@@ -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
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..f79c9a3
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/repo-go.iml b/.idea/repo-go.iml
new file mode 100644
index 0000000..5e764c4
--- /dev/null
+++ b/.idea/repo-go.iml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..907776b
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,26 @@
+module github.com/spacetab-io/pgrepo-go
+
+go 1.18
+
+require (
+ github.com/Masterminds/squirrel v1.5.3 // indirect
+ github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect
+ github.com/davecgh/go-spew v1.1.1 // indirect
+ github.com/jackc/pgpassfile v1.0.0 // indirect
+ github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect
+ github.com/jackc/pgx-zap v0.0.0-20220909013905-c08a18c611dd // indirect
+ github.com/jackc/pgx/v5 v5.1.1 // indirect
+ github.com/jackc/puddle/v2 v2.1.2 // indirect
+ github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect
+ github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect
+ github.com/pmezard/go-difflib v1.0.0 // indirect
+ github.com/spacetab-io/configuration-structs-go/v2 v2.0.0-alpha4 // indirect
+ github.com/stretchr/testify v1.8.1 // indirect
+ go.uber.org/atomic v1.10.0 // indirect
+ go.uber.org/multierr v1.6.0 // indirect
+ go.uber.org/zap v1.23.0 // indirect
+ golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 // indirect
+ golang.org/x/sync v0.0.0-20220923202941-7f9b1623fab7 // indirect
+ golang.org/x/text v0.3.8 // indirect
+ gopkg.in/yaml.v3 v3.0.1 // indirect
+)
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000..207814a
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,54 @@
+github.com/Masterminds/squirrel v1.5.3 h1:YPpoceAcxuzIljlr5iWpNKaql7hLeG1KLSrhvdHpkZc=
+github.com/Masterminds/squirrel v1.5.3/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10=
+github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ=
+github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/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-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg=
+github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
+github.com/jackc/pgx-zap v0.0.0-20220909013905-c08a18c611dd h1:M+2auCrSjdkb5QTP1KMF5UguRJwhaotv65kp35fmvYs=
+github.com/jackc/pgx-zap v0.0.0-20220909013905-c08a18c611dd/go.mod h1:MgU77f9s+s9Xl39aYLbOmxd5iKzeIyD5pdnHERuCquE=
+github.com/jackc/pgx/v5 v5.1.1 h1:pZD79K1SYv8wc2HmCQA6VdmRQi7/OtCfv9bM3WAXUYA=
+github.com/jackc/pgx/v5 v5.1.1/go.mod h1:Ptn7zmohNsWEsdxRawMzk3gaKma2obW+NWTnKa0S4nk=
+github.com/jackc/puddle/v2 v2.1.2 h1:0f7vaaXINONKTsxYDn4otOAiJanX/BMeAtY//BXqzlg=
+github.com/jackc/puddle/v2 v2.1.2/go.mod h1:2lpufsF5mRHO6SuZkm0fNYxM6SWHfvyFj62KwNzgels=
+github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq6+3iTQz8KNCLtVX6idSoTLdUw=
+github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o=
+github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk=
+github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/spacetab-io/configuration-structs-go/v2 v2.0.0-alpha4 h1:WxpQZOZ960I+YoC9ZUgz8rUPQHfDhtC6GGAd9fvUQig=
+github.com/spacetab-io/configuration-structs-go/v2 v2.0.0-alpha4/go.mod h1:/qyni0G7nIAu2Hdp7VW3p+RwjhwvIKJtKdbbN2osywE=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
+github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
+github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
+github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
+go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
+go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
+go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
+go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
+go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
+go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
+go.uber.org/zap v1.23.0 h1:OjGQ5KQDEUawVHxNwQgPpiypGHOxo2mNZsOqTak4fFY=
+go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY=
+golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 h1:Y/gsMcFOcR+6S6f3YeMKl5g+dZMEWqcz5Czj/GWYbkM=
+golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+golang.org/x/sync v0.0.0-20220923202941-7f9b1623fab7 h1:ZrnxWX62AgTKOSagEqxvb3ffipvEDX2pl7E1TdqLqIc=
+golang.org/x/sync v0.0.0-20220923202941-7f9b1623fab7/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY=
+golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/interface.go b/interface.go
new file mode 100644
index 0000000..6378c43
--- /dev/null
+++ b/interface.go
@@ -0,0 +1,41 @@
+package repo
+
+import (
+ "context"
+
+ "github.com/Masterminds/squirrel"
+ "github.com/jackc/pgx/v5"
+ "github.com/jackc/pgx/v5/pgxpool"
+)
+
+type RepositoryInterface interface {
+ GetReader() *pgxpool.Pool
+ GetWriter() *pgxpool.Pool
+ GetRepoName() string
+
+ GetPrefix() string
+ TableName(args ...string) string
+ TableNameWithoutPrefix() string
+ Column(columnName string, prefix ...string) string
+ ColumnWithoutPrefix(columnName string) string
+ Columns(specificColumns ...string) []string
+ ColumnsWithoutPrefix(specificCols ...string) []string
+ ColumnExists(column string) bool
+
+ Join(foreignColumns, thisTableColumn string, additionalWheres ...squirrel.Sqlizer) (string, []any)
+ JoinOn(prefix string, on string) string
+
+ ReadRow(ctx context.Context, _sql string, args []any) (*QueryResults, error)
+ ReadRows(ctx context.Context, _sql string, args []any) (*QueryResults, error)
+ WriteData(ctx context.Context, _sql string, args []any) error
+ WriteDataAndReadRow(ctx context.Context, _sql string, args []any) (*QueryResults, error)
+ WriteDataAndReadRows(ctx context.Context, _sql string, args []any) (*QueryResults, error)
+
+ ReadRowsError(err error) error
+ ScanRowError(err error) error
+ ScanRowsError(err error) error
+ ScanCountRow(row pgx.Row) (int, error)
+
+ TsVectorFromColumn(lang ColumnsLang, columns ...string) squirrel.Sqlizer
+ TsVectorFromData(lang ColumnsLang, args ...string) squirrel.Sqlizer
+}
diff --git a/main.functions.internal_test.go b/main.functions.internal_test.go
new file mode 100644
index 0000000..934e954
--- /dev/null
+++ b/main.functions.internal_test.go
@@ -0,0 +1,53 @@
+package repo
+
+import (
+ "testing"
+
+ "github.com/Masterminds/squirrel"
+ "github.com/stretchr/testify/assert"
+ "gitlab.worldskills.ru/worldskills/dpws/etc.git/v5/configuration"
+)
+
+var testLimits = &configuration.Limits{
+ TitleLen: 4096,
+ TextLen: 12800,
+}
+
+func TestRepositoryStuff_tsVector(t *testing.T) {
+ type testCase struct {
+ name string
+ in []string
+ exp squirrel.Sqlizer
+ }
+
+ tcs := []testCase{
+ {
+ name: "один аргумент",
+ in: []string{"question"},
+ exp: squirrel.Expr(`to_tsvector('russian', lower(question))`),
+ },
+ {
+ name: "два аргумента",
+ in: []string{"question", "answer"},
+ exp: squirrel.Expr(`to_tsvector('russian', lower(question || ' ' || answer))`),
+ },
+ {
+ name: "без аргументов",
+ in: nil,
+ exp: nil,
+ },
+ }
+
+ t.Parallel()
+
+ for _, tc := range tcs {
+ tc := tc
+ t.Run(tc.name, func(t *testing.T) {
+ t.Parallel()
+
+ r := RepositoryStuff{}
+
+ assert.Equal(t, tc.exp, r.tsVectorFromColumn(ColumnLangRu, tc.in...))
+ })
+ }
+}
diff --git a/postgresConnect.go b/postgresConnect.go
new file mode 100644
index 0000000..e7fb53d
--- /dev/null
+++ b/postgresConnect.go
@@ -0,0 +1,65 @@
+package repo
+
+import (
+ "context"
+ "fmt"
+
+ zapadapter "github.com/jackc/pgx-zap"
+ "github.com/jackc/pgx/v5"
+ "github.com/jackc/pgx/v5/pgxpool"
+ "github.com/jackc/pgx/v5/tracelog"
+ "github.com/spacetab-io/configuration-structs-go/v2/contracts"
+ "go.uber.org/zap"
+)
+
+const (
+ failureCode = 1
+)
+
+func PGConnect(cfg contracts.DatabaseCfgInterface, logger *zap.Logger, logLvl string) (*pgxpool.Pool, error) {
+ pgxConfig, err := pgxpool.ParseConfig(cfg.GetDSN())
+ if err != nil {
+ return nil, err
+ }
+
+ pgxConfig.MaxConnLifetime, pgxConfig.MaxConns, pgxConfig.MinConns = cfg.GetConnectionParams()
+
+ pgxConfig.ConnConfig.RuntimeParams = map[string]string{"standard_conforming_strings": "on"}
+ pgxConfig.AfterConnect = func(ctx context.Context, conn *pgx.Conn) error {
+ // Так как БД esat находится на той же СУБД, что и de-api, мы получаем
+ // наследование глобального datestyle и на бд esat. Чтобы поменять datestyle,
+ // нужно поменять в конфиге postgres для всех БД, что может убить работу
+ // de-api. Для того чтобы обойти это, мы выставим datestyle для коннекта,
+ // который будет использовать только esat. И волки целы и овцы сыты.
+ // Но конфигурацию БД нужно бы поменять...
+ rows, err := conn.Query(ctx, "SET datestyle = 'ISO, DMY'")
+ if err != nil {
+ return fmt.Errorf("AfterConnect SET datestyle error: %w", err)
+ }
+
+ rows.Close()
+
+ return nil
+ }
+
+ trLevel, err := tracelog.LogLevelFromString(logLvl)
+ if err != nil {
+ return nil, err
+ }
+
+ pgxConfig.ConnConfig.Tracer = &tracelog.TraceLog{
+ Logger: zapadapter.NewLogger(logger.WithOptions(zap.WithCaller(false))),
+ LogLevel: trLevel,
+ }
+
+ db, err := pgxpool.NewWithConfig(context.Background(), pgxConfig)
+ if err != nil {
+ return nil, err
+ }
+
+ if err := db.Ping(context.Background()); err != nil {
+ return nil, err
+ }
+
+ return db, nil
+}
diff --git a/postgresRepo.go b/postgresRepo.go
new file mode 100644
index 0000000..f546534
--- /dev/null
+++ b/postgresRepo.go
@@ -0,0 +1,339 @@
+package repo
+
+import (
+ "context"
+ "fmt"
+ "strings"
+
+ "github.com/Masterminds/squirrel"
+ "github.com/jackc/pgx/v5"
+ "github.com/jackc/pgx/v5/pgxpool"
+)
+
+type PostgresRepo struct {
+ name string
+ reader *pgxpool.Pool
+ writer *pgxpool.Pool
+ table string
+ prefix string
+ cols []string
+}
+
+type QueryResults struct {
+ poolCon *pgxpool.Conn
+ rows pgx.Rows
+ row pgx.Row
+}
+
+type ColumnsLang string
+
+const (
+ ColumnLangRu ColumnsLang = "russian"
+ ColumnLangEn ColumnsLang = "english"
+)
+
+func NewPostgresRepo(reader, writer *pgxpool.Pool, name, table, prefix string, columns []string) *PostgresRepo {
+ return &PostgresRepo{
+ name: name,
+ reader: reader,
+ writer: writer,
+ table: table,
+ prefix: prefix,
+ cols: columns,
+ }
+}
+
+func (r *PostgresRepo) SetRepoName(name string) {
+ r.name = name
+}
+
+func (r PostgresRepo) GetRepoName() string {
+ return r.name
+}
+
+func (r *PostgresRepo) SetReader(reader *pgxpool.Pool) {
+ r.reader = reader
+}
+
+func (r PostgresRepo) GetReader() *pgxpool.Pool {
+ return r.reader
+}
+
+func (r *PostgresRepo) SetWriter(writer *pgxpool.Pool) {
+ r.writer = writer
+}
+
+func (r PostgresRepo) GetWriter() *pgxpool.Pool {
+ return r.writer
+}
+
+func (r *PostgresRepo) SetTable(table string) {
+ r.table = table
+}
+
+func (r *PostgresRepo) GetTable() string {
+ return r.table
+}
+
+func (r *PostgresRepo) SetPrefix(prefix string) {
+ r.prefix = prefix
+}
+
+func (r PostgresRepo) GetPrefix() string {
+ return r.prefix
+}
+
+func (r *PostgresRepo) SetCols(cols []string) {
+ r.cols = cols
+}
+
+func (r *PostgresRepo) GetCols() []string {
+ return r.cols
+}
+
+func (r PostgresRepo) Column(name string, args ...string) string {
+ prefix := r.prefix
+
+ if len(args) >= 1 {
+ prefix = args[0]
+ }
+
+ if prefix != "" {
+ return fmt.Sprintf("%s.%s", prefix, name)
+ }
+
+ return name
+}
+
+func (r PostgresRepo) ColumnWithoutPrefix(name string) string {
+ str := strings.Split(name, ".")
+ if len(str) <= 1 {
+ return name
+ }
+
+ return str[1]
+}
+
+func (r PostgresRepo) Columns(specificColumns ...string) []string {
+ cols := r.cols
+ if len(specificColumns) > 0 {
+ cols = specificColumns
+ }
+
+ cc := make([]string, 0, len(cols))
+
+ for _, c := range r.cols {
+ cc = append(cc, fmt.Sprintf("%s.%s", r.prefix, c))
+ }
+
+ return cc
+}
+
+func (r PostgresRepo) ColumnsWithoutPrefix(specificColumns ...string) []string {
+ cols := make([]string, 0, len(r.Columns(specificColumns...)))
+
+ if len(specificColumns) == 0 {
+ specificColumns = r.Columns()
+ }
+
+ for _, col := range specificColumns {
+ str := strings.Split(col, ".")
+ if len(str) <= 1 {
+ cols = append(cols, col)
+
+ continue
+ }
+
+ cols = append(cols, str[1])
+ }
+
+ return cols
+}
+
+func (r PostgresRepo) ColumnExists(column string) bool {
+ columnSlice := strings.Split(column, ".")
+
+ if len(columnSlice) == 2 { //nolint:gomnd // структура наименования с определением названия таблицы -- 2 элемента
+ column = columnSlice[1]
+ }
+
+ for _, col := range r.ColumnsWithoutPrefix() {
+ if col == column {
+ return true
+ }
+ }
+
+ return false
+}
+
+func (r PostgresRepo) TableName(args ...string) string {
+ prefix := r.prefix
+
+ if len(args) >= 1 {
+ prefix = args[0]
+ }
+
+ return fmt.Sprintf("%s %s", r.table, prefix)
+}
+
+func (r PostgresRepo) TableNameWithoutPrefix() string {
+ return r.table
+}
+
+func (r PostgresRepo) JoinOn(prefix string, on string) string {
+ if prefix == "" {
+ prefix = r.prefix
+ }
+
+ return r.TableName(prefix) + " on " + on
+}
+
+func (r PostgresRepo) Join(foreignColumns, thisTableColumn string, additionalWheres ...squirrel.Sqlizer) (string, []any) {
+ stmt := fmt.Sprintf("%s on %s = %s", r.TableName(), thisTableColumn, foreignColumns)
+
+ if len(additionalWheres) == 0 {
+ return stmt, nil
+ }
+
+ args := make([]any, 0)
+
+ for _, where := range additionalWheres {
+ if where == nil {
+ continue
+ }
+
+ q, moreArgs, _ := where.ToSql() //nolint:errcheck // полагаем, что ошибок в формировании подзапроса не будет
+
+ stmt += fmt.Sprintf(" AND %s", q)
+
+ if len(moreArgs) > 0 {
+ args = append(args, moreArgs...)
+ }
+ }
+
+ return stmt, args
+}
+
+func (r PostgresRepo) ReadRows(ctx context.Context, _sql string, args []any) (*QueryResults, error) {
+ db, err := r.GetReader().Acquire(ctx)
+ if err != nil {
+ return nil, fmt.Errorf("%s.readRows Acquire error: %w", r.GetRepoName(), err)
+ }
+
+ rows, err := db.Query(ctx, _sql, args...)
+ if err != nil {
+ return nil, fmt.Errorf("%s.readRows Query error: %w", r.GetRepoName(), err)
+ }
+
+ return &QueryResults{poolCon: db, rows: rows}, nil
+}
+
+func (r PostgresRepo) ReadRow(ctx context.Context, _sql string, args []any) (*QueryResults, error) {
+ db, err := r.GetReader().Acquire(ctx)
+ if err != nil {
+ return nil, fmt.Errorf("%s.ReadRow Acquire error: %w", r.GetRepoName(), err)
+ }
+
+ return &QueryResults{poolCon: db, row: db.QueryRow(ctx, _sql, args...)}, nil
+}
+
+func (r PostgresRepo) WriteData(ctx context.Context, _sql string, args []any) error {
+ db, err := r.GetWriter().Acquire(ctx)
+ if err != nil {
+ return fmt.Errorf("%s.writeData Acquire error: %w", r.GetRepoName(), err)
+ }
+
+ defer db.Release()
+
+ if _, err := db.Exec(ctx, _sql, args...); err != nil {
+ return fmt.Errorf("%s.writeData query error: %w", r.GetRepoName(), err)
+ }
+
+ return nil
+}
+
+func (r PostgresRepo) WriteDataAndReadRow(ctx context.Context, _sql string, args []any) (*QueryResults, error) {
+ db, err := r.GetWriter().Acquire(ctx)
+ if err != nil {
+ return nil, fmt.Errorf("%s.WriteDataAndReadRow Acquire error: %w", r.GetRepoName(), err)
+ }
+
+ return &QueryResults{poolCon: db, row: db.QueryRow(ctx, _sql, args...)}, nil
+}
+
+func (r PostgresRepo) WriteDataAndReadRows(ctx context.Context, _sql string, args []any) (*QueryResults, error) {
+ db, err := r.GetWriter().Acquire(ctx)
+ if err != nil {
+ return nil, fmt.Errorf("%s.WriteDataAndReadRows Acquire error: %w", r.GetRepoName(), err)
+ }
+
+ rows, err := db.Query(ctx, _sql, args...)
+ if err != nil {
+ return nil, fmt.Errorf("%s.WriteDataAndReadRows Query error: %w", r.GetRepoName(), err)
+ }
+
+ return &QueryResults{poolCon: db, rows: rows}, nil
+}
+
+func (r PostgresRepo) ReadRowsError(err error) error {
+ return fmt.Errorf("%s row scan error: %w", r.GetRepoName(), err)
+}
+
+func (r PostgresRepo) ScanRowError(err error) error {
+ return fmt.Errorf("%s row scan error: %w", r.GetRepoName(), err)
+}
+
+func (r PostgresRepo) ScanRowsError(err error) error {
+ return fmt.Errorf("%s rows scan error: %w", r.GetRepoName(), err)
+}
+
+func (r PostgresRepo) ScanCountRow(row pgx.Row) (int, error) {
+ var count int
+
+ if err := row.Scan(&count); err != nil {
+ return 0, err
+ }
+
+ return count, nil
+}
+
+func (r PostgresRepo) TsVectorFromColumn(lang ColumnsLang, columns ...string) squirrel.Sqlizer {
+ if len(columns) == 0 {
+ return nil
+ }
+
+ return squirrel.Expr(fmt.Sprintf(
+ `to_tsvector('%s', lower(%s))`,
+ lang,
+ strings.Join(columns, " || ' ' || "),
+ ))
+}
+
+func (r PostgresRepo) TsVectorFromData(lang ColumnsLang, args ...string) squirrel.Sqlizer {
+ if len(args) == 0 {
+ return nil
+ }
+
+ return squirrel.Expr(fmt.Sprintf(
+ `to_tsvector('%s', lower('%s'))`,
+ lang,
+ strings.Join(args, "' || ' ' || '"),
+ // r.collate(lang),
+ ))
+}
+
+func (qr QueryResults) Close() {
+ if qr.rows != nil {
+ qr.rows.Close()
+ }
+
+ qr.poolCon.Release()
+}
+
+func (qr *QueryResults) GetRows() pgx.Rows {
+ return qr.rows
+}
+
+func (qr *QueryResults) GetRow() pgx.Row {
+ return qr.row
+}