Skip to content

Commit 4a88cad

Browse files
committed
new impl func interfaces
1 parent c71b6c9 commit 4a88cad

16 files changed

+210
-151
lines changed

go.work.sum

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,13 @@
1+
github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a h1:MISbI8sU/PSK/ztvmWKFcI7UGb5/HQT7B+i3a2myKgI=
12
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
3+
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f h1:3BSP1Tbs2djlpprl7wCLuiqMaUh5SJkkzI2gDs+FgLs=
4+
github.com/jaytaylor/html2text v0.0.0-20211105163654-bc68cce691ba h1:QFQpJdgbON7I0jr2hYW7Bs+XV0qjc3d5tZoDnRFnqTg=
5+
github.com/jhillyerd/enmime v0.10.1 h1:3VP8gFhK7R948YJBrna5bOgnTXEuPAoICo79kKkBKfA=
6+
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
7+
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
8+
github.com/rivo/uniseg v0.4.3 h1:utMvzDsuh3suAEnhH0RdHmoPbU648o6CvXxTx4SBMOw=
9+
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf h1:pvbZ0lM0XWPBqUKqFU8cmavspvIl9nulOYwdy6IFRRo=
10+
github.com/teamwork/tnef v0.0.0-20200108124832-7deabccfdb32 h1:j15wq0XPAY/HR/0+dtwUrIrF2ZTKbk7QIES2p4dAG+k=
211
github.com/ungerik/go-fs v0.0.0-20230206141012-abb864f815e3 h1:IEm9Je1L3HAIEwiXOuGgJuUPjXXHzf/1e704VyIbcGc=
12+
golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
13+
mvdan.cc/xurls/v2 v2.4.0 h1:tzxjVAj+wSBmDcF6zBB7/myTy3gX9xvi8Tyr28AuQgc=

impl/arrays.go

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,30 @@ func ShouldWrapForArrayScanning(v reflect.Value) bool {
3939
return false
4040
}
4141

42-
// IsNonDriverValuerSliceOrArray returns true if passed type
42+
// IsSliceOrArray returns true if passed value is a slice or array,
43+
// or a pointer to a slice or array and in case of a slice
44+
// not of type []byte.
45+
func IsSliceOrArray(value any) bool {
46+
if value == nil {
47+
return false
48+
}
49+
v := reflect.ValueOf(value)
50+
if v.Kind() == reflect.Ptr {
51+
if v.IsNil() {
52+
return false
53+
}
54+
v = v.Elem()
55+
}
56+
t := v.Type()
57+
k := t.Kind()
58+
return k == reflect.Slice && t != typeOfByteSlice || k == reflect.Array
59+
}
60+
61+
// IsNonDriverValuerSliceOrArrayType returns true if passed type
4362
// does not implement driver.Valuer and is a slice or array,
4463
// or a pointer to a slice or array and in case of a slice
4564
// not of type []byte.
46-
func IsNonDriverValuerSliceOrArray(t reflect.Type) bool {
65+
func IsNonDriverValuerSliceOrArrayType(t reflect.Type) bool {
4766
if t == nil || t.Implements(typeOfDriverValuer) {
4867
return false
4968
}

impl/arrays_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ func TestShouldWrapForArrayScanning(t *testing.T) {
3636
}
3737
}
3838

39-
func TestIsNonDriverValuerSliceOrArray(t *testing.T) {
39+
func TestIsNonDriverValuerSliceOrArrayType(t *testing.T) {
4040
tests := []struct {
4141
t reflect.Type
4242
want bool
@@ -60,8 +60,8 @@ func TestIsNonDriverValuerSliceOrArray(t *testing.T) {
6060
{t: reflect.TypeOf((*[][]byte)(nil)), want: true},
6161
}
6262
for _, tt := range tests {
63-
got := IsNonDriverValuerSliceOrArray(tt.t)
64-
assert.Equalf(t, tt.want, got, "IsNonDriverValuerSliceOrArray(%s)", tt.t)
63+
got := IsNonDriverValuerSliceOrArrayType(tt.t)
64+
assert.Equalf(t, tt.want, got, "IsNonDriverValuerSliceOrArrayType(%s)", tt.t)
6565
}
6666
}
6767

impl/errors.go

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -26,17 +26,5 @@ type errWithQuery struct {
2626
func (e errWithQuery) Unwrap() error { return e.err }
2727

2828
func (e errWithQuery) Error() string {
29-
return fmt.Sprintf("%s from query: %s", e.err, FormatQuery(e.query, e.argFmt, e.args...))
30-
}
31-
32-
func combineErrors(prim, sec error) error {
33-
switch {
34-
case prim != nil && sec != nil:
35-
return fmt.Errorf("%w\n%s", prim, sec)
36-
case prim != nil:
37-
return prim
38-
case sec != nil:
39-
return sec
40-
}
41-
return nil
29+
return fmt.Sprintf("%s from query: %s", e.err, FormatQuery(e.query, e.args, e.argFmt))
4230
}

impl/exec.go

Lines changed: 0 additions & 16 deletions
This file was deleted.

impl/format.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ func FormatValue(val any) (string, error) {
7777
return fmt.Sprint(val), nil
7878
}
7979

80-
func FormatQuery(query, argFmt string, args ...any) string {
80+
func FormatQuery(query string, args []any, argFmt string) string {
8181
for i := len(args) - 1; i >= 0; i-- {
8282
placeholder := fmt.Sprintf(argFmt, i+1)
8383
value, err := FormatValue(args[i])
@@ -128,8 +128,8 @@ func FormatQuery(query, argFmt string, args ...any) string {
128128
// to DDL and other statements that do not accept parameters) to be used as part
129129
// of an SQL statement. For example:
130130
//
131-
// exp_date := pq.QuoteLiteral("2023-01-05 15:00:00Z")
132-
// err := db.Exec(fmt.Sprintf("CREATE ROLE my_user VALID UNTIL %s", exp_date))
131+
// exp_date := pq.QuoteLiteral("2023-01-05 15:00:00Z")
132+
// err := db.Exec(fmt.Sprintf("CREATE ROLE my_user VALID UNTIL %s", exp_date))
133133
//
134134
// Any single quotes in name will be escaped. Any backslashes (i.e. "\") will be
135135
// replaced by two backslashes (i.e. "\\") and the C-style escape identifier

impl/format_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ WHERE
9090
}
9191
for _, tt := range tests {
9292
t.Run(tt.name, func(t *testing.T) {
93-
if got := FormatQuery(tt.query, tt.argFmt, tt.args...); got != tt.want {
93+
if got := FormatQuery(tt.query, tt.args, tt.argFmt); got != tt.want {
9494
t.Errorf("FormatQuery():\n%q\nWant:\n%q", got, tt.want)
9595
}
9696
})

impl/genericconnection.go

Lines changed: 21 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package impl
33
import (
44
"context"
55
"database/sql"
6+
"database/sql/driver"
67
"errors"
78
"time"
89

@@ -13,7 +14,7 @@ import (
1314
// for an existing sql.DB connection.
1415
// argFmt is the format string for argument placeholders like "?" or "$%d"
1516
// that will be replaced error messages to format a complete query.
16-
func NewGenericConnection(ctx context.Context, db *sql.DB, config *sqldb.Config, listener sqldb.Listener, structFieldMapper sqldb.StructFieldMapper, validateColumnName func(string) error, argFmt string) sqldb.Connection {
17+
func NewGenericConnection(ctx context.Context, db *sql.DB, config *sqldb.Config, listener sqldb.Listener, structFieldMapper sqldb.StructFieldMapper, validateColumnName func(string) error, converter driver.ValueConverter, argFmt string) sqldb.Connection {
1718
if listener == nil {
1819
listener = sqldb.UnsupportedListener()
1920
}
@@ -24,6 +25,7 @@ func NewGenericConnection(ctx context.Context, db *sql.DB, config *sqldb.Config,
2425
listener: listener,
2526
structFieldMapper: structFieldMapper,
2627
validateColumnName: validateColumnName,
28+
converter: converter,
2729
argFmt: argFmt,
2830
}
2931
}
@@ -35,6 +37,7 @@ type genericConn struct {
3537
listener sqldb.Listener
3638
structFieldMapper sqldb.StructFieldMapper
3739
validateColumnName func(string) error
40+
converter driver.ValueConverter
3841
argFmt string
3942

4043
tx *sql.Tx
@@ -109,31 +112,31 @@ func (conn *genericConn) queryer() Queryer {
109112
}
110113

111114
func (conn *genericConn) Exec(query string, args ...any) error {
112-
return Exec(conn.ctx, conn.execer(), conn.argFmt, query, args)
115+
return Exec(conn.ctx, conn.execer(), query, args, conn.converter, conn.argFmt)
113116
}
114117

115118
func (conn *genericConn) QueryRow(query string, args ...any) sqldb.RowScanner {
116-
return QueryRow(conn.ctx, conn.queryer(), conn.structFieldMapper, conn.argFmt, query, args)
119+
return QueryRow(conn.ctx, conn.queryer(), query, args, conn.converter, conn.argFmt, conn.structFieldMapper)
117120
}
118121

119122
func (conn *genericConn) QueryRows(query string, args ...any) sqldb.RowsScanner {
120-
return QueryRows(conn.ctx, conn.queryer(), conn.structFieldMapper, conn.argFmt, query, args)
123+
return QueryRows(conn.ctx, conn.queryer(), query, args, conn.converter, conn.argFmt, conn.structFieldMapper)
121124
}
122125

123126
func (conn *genericConn) Insert(table string, columValues sqldb.Values) error {
124-
return Insert(conn.ctx, conn.execer(), table, conn.argFmt, columValues)
127+
return Insert(conn.ctx, conn.execer(), table, columValues, conn.converter, conn.argFmt)
125128
}
126129

127130
func (conn *genericConn) InsertUnique(table string, values sqldb.Values, onConflict string) (inserted bool, err error) {
128-
return InsertUnique(conn.ctx, conn.queryer(), conn.structFieldMapper, conn.argFmt, table, values, onConflict)
131+
return InsertUnique(conn.ctx, conn.queryer(), table, values, onConflict, conn.converter, conn.argFmt, conn.structFieldMapper)
129132
}
130133

131134
func (conn *genericConn) InsertReturning(table string, values sqldb.Values, returning string) sqldb.RowScanner {
132-
return InsertReturning(conn.ctx, conn.queryer(), conn.structFieldMapper, conn.argFmt, table, values, returning)
135+
return InsertReturning(conn.ctx, conn.queryer(), table, values, returning, conn.converter, conn.argFmt, conn.structFieldMapper)
133136
}
134137

135138
func (conn *genericConn) InsertStruct(table string, rowStruct any, ignoreColumns ...sqldb.ColumnFilter) error {
136-
return InsertStruct(conn.ctx, conn.execer(), table, rowStruct, conn.structFieldMapper, conn.argFmt, ignoreColumns)
139+
return InsertStruct(conn.ctx, conn.execer(), table, rowStruct, conn.structFieldMapper, ignoreColumns, conn.converter, conn.argFmt)
137140
}
138141

139142
func (conn *genericConn) InsertStructs(table string, rowStructs any, ignoreColumns ...sqldb.ColumnFilter) error {
@@ -142,23 +145,23 @@ func (conn *genericConn) InsertStructs(table string, rowStructs any, ignoreColum
142145
}
143146

144147
func (conn *genericConn) InsertUniqueStruct(table string, rowStruct any, onConflict string, ignoreColumns ...sqldb.ColumnFilter) (inserted bool, err error) {
145-
return InsertUniqueStruct(conn.ctx, conn.queryer(), conn.structFieldMapper, conn.argFmt, table, rowStruct, onConflict, ignoreColumns)
148+
return InsertUniqueStruct(conn.ctx, conn.queryer(), conn.structFieldMapper, table, rowStruct, onConflict, ignoreColumns, conn.converter, conn.argFmt)
146149
}
147150

148151
func (conn *genericConn) Update(table string, values sqldb.Values, where string, args ...any) error {
149-
return Update(conn.ctx, conn.execer(), table, values, where, conn.argFmt, args)
152+
return Update(conn.ctx, conn.execer(), table, values, where, args, conn.converter, conn.argFmt)
150153
}
151154

152155
func (conn *genericConn) UpdateReturningRow(table string, values sqldb.Values, returning, where string, args ...any) sqldb.RowScanner {
153-
return UpdateReturningRow(conn.ctx, conn.queryer(), conn.structFieldMapper, conn.argFmt, table, values, returning, where, args)
156+
return UpdateReturningRow(conn.ctx, conn.queryer(), table, values, returning, where, args, conn.converter, conn.argFmt, conn.structFieldMapper)
154157
}
155158

156159
func (conn *genericConn) UpdateReturningRows(table string, values sqldb.Values, returning, where string, args ...any) sqldb.RowsScanner {
157-
return UpdateReturningRows(conn.ctx, conn.queryer(), conn.structFieldMapper, conn.argFmt, table, values, returning, where, args)
160+
return UpdateReturningRows(conn.ctx, conn.queryer(), table, values, returning, where, args, conn.converter, conn.argFmt, conn.structFieldMapper)
158161
}
159162

160163
func (conn *genericConn) UpdateStruct(table string, rowStruct any, ignoreColumns ...sqldb.ColumnFilter) error {
161-
return UpdateStruct(conn.ctx, conn.execer(), table, rowStruct, conn.structFieldMapper, conn.argFmt, ignoreColumns)
164+
return UpdateStruct(conn.ctx, conn.execer(), table, rowStruct, conn.structFieldMapper, ignoreColumns, conn.converter, conn.argFmt)
162165
}
163166

164167
func (conn *genericConn) UpsertStruct(table string, rowStruct any, ignoreColumns ...sqldb.ColumnFilter) error {
@@ -182,18 +185,11 @@ func (conn *genericConn) Begin(opts *sql.TxOptions, no uint64) (sqldb.Connection
182185
if err != nil {
183186
return nil, err
184187
}
185-
return &genericConn{
186-
ctx: conn.ctx,
187-
db: conn.db, // needed for PingContext, Stats
188-
config: conn.config,
189-
listener: conn.listener,
190-
structFieldMapper: conn.structFieldMapper,
191-
argFmt: conn.argFmt,
192-
validateColumnName: conn.validateColumnName,
193-
tx: tx,
194-
txOptions: opts,
195-
txNo: no,
196-
}, nil
188+
txConn := conn.clone()
189+
txConn.tx = tx
190+
txConn.txOptions = opts
191+
txConn.txNo = no
192+
return txConn, nil
197193
}
198194

199195
func (conn *genericConn) Commit() error {

impl/insert.go

Lines changed: 19 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package impl
22

33
import (
44
"context"
5+
"database/sql/driver"
56
"fmt"
67
"reflect"
78
"strings"
@@ -10,25 +11,22 @@ import (
1011
)
1112

1213
// Insert a new row into table using the values.
13-
func Insert(ctx context.Context, conn Execer, table, argFmt string, values sqldb.Values) error {
14+
func Insert(ctx context.Context, conn Execer, table string, values sqldb.Values, converter driver.ValueConverter, argFmt string) error {
1415
if len(values) == 0 {
1516
return fmt.Errorf("Insert into table %s: no values", table)
1617
}
1718

18-
names, vals := values.Sorted()
19-
b := strings.Builder{}
20-
writeInsertQuery(&b, table, argFmt, names)
21-
query := b.String()
22-
23-
_, err := conn.ExecContext(ctx, query, vals...)
19+
names, args := values.Sorted()
20+
query := strings.Builder{}
21+
writeInsertQuery(&query, table, argFmt, names)
2422

25-
return WrapNonNilErrorWithQuery(err, query, argFmt, vals)
23+
return Exec(ctx, conn, query.String(), args, converter, argFmt)
2624
}
2725

2826
// InsertUnique inserts a new row into table using the passed values
2927
// or does nothing if the onConflict statement applies.
3028
// Returns if a row was inserted.
31-
func InsertUnique(ctx context.Context, conn Queryer, mapper sqldb.StructFieldMapper, argFmt, table string, values sqldb.Values, onConflict string) (inserted bool, err error) {
29+
func InsertUnique(ctx context.Context, conn Queryer, table string, values sqldb.Values, onConflict string, converter driver.ValueConverter, argFmt string, mapper sqldb.StructFieldMapper) (inserted bool, err error) {
3230
if len(values) == 0 {
3331
return false, fmt.Errorf("InsertUnique into table %s: no values", table)
3432
}
@@ -42,13 +40,13 @@ func InsertUnique(ctx context.Context, conn Queryer, mapper sqldb.StructFieldMap
4240
writeInsertQuery(&query, table, argFmt, names)
4341
fmt.Fprintf(&query, " ON CONFLICT (%s) DO NOTHING RETURNING TRUE", onConflict)
4442

45-
err = QueryRow(ctx, conn, mapper, argFmt, query.String(), vals).Scan(&inserted)
43+
err = QueryRow(ctx, conn, query.String(), vals, converter, argFmt, mapper).Scan(&inserted)
4644
return inserted, sqldb.ReplaceErrNoRows(err, nil)
4745
}
4846

4947
// InsertReturning inserts a new row into table using values
5048
// and returns values from the inserted row listed in returning.
51-
func InsertReturning(ctx context.Context, conn Queryer, mapper sqldb.StructFieldMapper, argFmt, table string, values sqldb.Values, returning string) sqldb.RowScanner {
49+
func InsertReturning(ctx context.Context, conn Queryer, table string, values sqldb.Values, returning string, converter driver.ValueConverter, argFmt string, mapper sqldb.StructFieldMapper) sqldb.RowScanner {
5250
if len(values) == 0 {
5351
return sqldb.RowScannerWithError(fmt.Errorf("InsertReturning into table %s: no values", table))
5452
}
@@ -59,7 +57,7 @@ func InsertReturning(ctx context.Context, conn Queryer, mapper sqldb.StructField
5957
query.WriteString(" RETURNING ")
6058
query.WriteString(returning)
6159

62-
return QueryRow(ctx, conn, mapper, argFmt, query.String(), vals)
60+
return QueryRow(ctx, conn, query.String(), vals, converter, argFmt, mapper)
6361
}
6462

6563
func writeInsertQuery(w *strings.Builder, table, argFmt string, names []string) {
@@ -85,27 +83,24 @@ func writeInsertQuery(w *strings.Builder, table, argFmt string, names []string)
8583
// InsertStruct inserts a new row into table using the connection's
8684
// StructFieldMapper to map struct fields to column names.
8785
// Optional ColumnFilter can be passed to ignore mapped columns.
88-
func InsertStruct(ctx context.Context, conn Execer, table string, rowStruct any, mapper sqldb.StructFieldMapper, argFmt string, ignoreColumns []sqldb.ColumnFilter) error {
86+
func InsertStruct(ctx context.Context, conn Execer, table string, rowStruct any, mapper sqldb.StructFieldMapper, ignoreColumns []sqldb.ColumnFilter, converter driver.ValueConverter, argFmt string) error {
8987
columns, vals, err := insertStructValues(table, rowStruct, mapper, ignoreColumns)
9088
if err != nil {
9189
return err
9290
}
9391

94-
var b strings.Builder
95-
writeInsertQuery(&b, table, argFmt, columns)
96-
query := b.String()
97-
98-
_, err = conn.ExecContext(ctx, query, vals...)
92+
var query strings.Builder
93+
writeInsertQuery(&query, table, argFmt, columns)
9994

100-
return WrapNonNilErrorWithQuery(err, query, argFmt, vals)
95+
return Exec(ctx, conn, query.String(), vals, converter, argFmt)
10196
}
10297

10398
// InsertUniqueStruct inserts a new row into table using the connection's
10499
// StructFieldMapper to map struct fields to column names.
105100
// Optional ColumnFilter can be passed to ignore mapped columns.
106101
// Does nothing if the onConflict statement applies
107102
// and returns if a row was inserted.
108-
func InsertUniqueStruct(ctx context.Context, conn Queryer, mapper sqldb.StructFieldMapper, argFmt string, table string, rowStruct any, onConflict string, ignoreColumns []sqldb.ColumnFilter) (inserted bool, err error) {
103+
func InsertUniqueStruct(ctx context.Context, conn Queryer, mapper sqldb.StructFieldMapper, table string, rowStruct any, onConflict string, ignoreColumns []sqldb.ColumnFilter, converter driver.ValueConverter, argFmt string) (inserted bool, err error) {
109104
columns, vals, err := insertStructValues(table, rowStruct, mapper, ignoreColumns)
110105
if err != nil {
111106
return false, err
@@ -115,12 +110,11 @@ func InsertUniqueStruct(ctx context.Context, conn Queryer, mapper sqldb.StructFi
115110
onConflict = onConflict[1 : len(onConflict)-1]
116111
}
117112

118-
var b strings.Builder
119-
writeInsertQuery(&b, table, argFmt, columns)
120-
fmt.Fprintf(&b, " ON CONFLICT (%s) DO NOTHING RETURNING TRUE", onConflict)
121-
query := b.String()
113+
var query strings.Builder
114+
writeInsertQuery(&query, table, argFmt, columns)
115+
fmt.Fprintf(&query, " ON CONFLICT (%s) DO NOTHING RETURNING TRUE", onConflict)
122116

123-
err = QueryRow(ctx, conn, mapper, argFmt, query, vals).Scan(&inserted)
117+
err = QueryRow(ctx, conn, query.String(), vals, converter, argFmt, mapper).Scan(&inserted)
124118
return inserted, sqldb.ReplaceErrNoRows(err, nil)
125119
}
126120

0 commit comments

Comments
 (0)