Skip to content

Commit 1b0a74a

Browse files
authored
Merge pull request #1 from bitbus/pr-some-help-fn
add With/WithTx/Withx/WithTxx fn for Conn/DB and add InExec/InGet/InS…
2 parents 47611e0 + 84af935 commit 1b0a74a

File tree

2 files changed

+304
-0
lines changed

2 files changed

+304
-0
lines changed

sqlx.go

+222
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package sqlx
22

33
import (
4+
"context"
45
"database/sql"
56
"database/sql/driver"
67
"errors"
@@ -86,6 +87,18 @@ type Execer interface {
8687
Exec(query string, args ...interface{}) (sql.Result, error)
8788
}
8889

90+
// QueryIn is an interface used by InGet and InSelect
91+
type QueryIn interface {
92+
Queryer
93+
In(query string, args ...interface{}) (string, []interface{}, error)
94+
}
95+
96+
// ExecIn is an interface used by MustInExec and InExec
97+
type ExecIn interface {
98+
Execer
99+
In(query string, args ...interface{}) (string, []interface{}, error)
100+
}
101+
89102
// Binder is an interface for something which can bind queries (Tx, DB)
90103
type binder interface {
91104
DriverName() string
@@ -343,6 +356,110 @@ func (db *DB) Beginx() (*Tx, error) {
343356
return &Tx{Tx: tx, driverName: db.driverName, unsafe: db.unsafe, Mapper: db.Mapper}, err
344357
}
345358

359+
// Begin starts a transaction and do the given handle. The default isolation level
360+
// is dependent on the driver.
361+
//
362+
// With uses context.Background internally; to specify the context, use
363+
// With Tx.
364+
func (db *DB) With(handle func(tx *sql.Tx) error) error {
365+
tx, err := db.Begin()
366+
if err != nil {
367+
return err
368+
}
369+
defer tx.Rollback()
370+
if err = handle(tx); err != nil {
371+
return err
372+
}
373+
return tx.Commit()
374+
}
375+
376+
// BeginTx starts a transaction and do the given handle.
377+
//
378+
// The provided context is used until the transaction is committed or rolled back.
379+
// If the context is canceled, the sql package will roll back
380+
// the transaction. Tx.Commit will return an error if the context provided to
381+
// BeginTx is canceled.
382+
//
383+
// The provided TxOptions is optional and may be nil if defaults should be used.
384+
// If a non-default isolation level is used that the driver doesn't support,
385+
// an error will be returned.
386+
func (db *DB) WithTx(ctx context.Context, opts *sql.TxOptions, handle func(tx *sql.Tx) error) error {
387+
tx, err := db.BeginTx(ctx, opts)
388+
if err != nil {
389+
return err
390+
}
391+
defer tx.Rollback()
392+
if err = handle(tx); err != nil {
393+
return err
394+
}
395+
return tx.Commit()
396+
}
397+
398+
// Withx begins a transaction and returns an *sqlx.Tx instead of an *sql.Tx and do the given handle.
399+
func (db *DB) Withx(handle func(tx *Tx) error) error {
400+
tx, err := db.Beginx()
401+
if err != nil {
402+
return err
403+
}
404+
defer tx.Rollback()
405+
if err = handle(tx); err != nil {
406+
return err
407+
}
408+
return tx.Commit()
409+
}
410+
411+
// WithTxx begins a transaction and returns an *sqlx.Tx instead of an
412+
// *sql.Tx and do the give handle.
413+
//
414+
// The provided context is used until the transaction is committed or rolled
415+
// back. If the context is canceled, the sql package will roll back the
416+
// transaction. Tx.Commit will return an error if the context provided to
417+
// BeginxContext is canceled.
418+
func (db *DB) WithTxx(ctx context.Context, opts *sql.TxOptions, handle func(tx *Tx) error) error {
419+
tx, err := db.BeginTxx(ctx, opts)
420+
if err != nil {
421+
return err
422+
}
423+
defer tx.Rollback()
424+
if err = handle(tx); err != nil {
425+
return err
426+
}
427+
return tx.Commit()
428+
}
429+
430+
// In expands slice values in args, returning the modified query string
431+
// and a new arg list that can be executed by a database. The `query` should
432+
// use the `?` bindVar. The return value uses had rebinded bindvar type.
433+
func (db *DB) In(query string, args ...any) (string, []any, error) {
434+
q, params, err := In(query, args...)
435+
if err != nil {
436+
return "", nil, err
437+
}
438+
return db.Rebind(q), params, nil
439+
}
440+
441+
// InExec executes a query without returning any rows for in.
442+
// The args are for any placeholder parameters in the query.
443+
//
444+
// InExec uses context.Background internally; to specify the context, use
445+
// ExecContext.
446+
func (db *DB) InExec(query string, args ...any) (sql.Result, error) {
447+
return InExec(db, query, args...)
448+
}
449+
450+
// InSelect using this DB but for in.
451+
// Any placeholder parameters are replaced with supplied args.
452+
func (db *DB) InSelect(dest any, query string, args ...any) error {
453+
return InSelect(db, dest, query, args...)
454+
}
455+
456+
// InGet using this DB but for in.
457+
// Any placeholder parameters are replaced with supplied args.
458+
// An error is returned if the result set is empty.
459+
func (db *DB) InGet(dest any, query string, args ...any) error {
460+
return InGet(db, dest, query, args...)
461+
}
462+
346463
// Queryx queries the database and returns an *sqlx.Rows.
347464
// Any placeholder parameters are replaced with supplied args.
348465
func (db *DB) Queryx(query string, args ...interface{}) (*Rows, error) {
@@ -366,6 +483,12 @@ func (db *DB) MustExec(query string, args ...interface{}) sql.Result {
366483
return MustExec(db, query, args...)
367484
}
368485

486+
// MustExec (panic) runs MustExec using this database for in.
487+
// Any placeholder parameters are replaced with supplied args.
488+
func (db *DB) MustInExec(query string, args ...interface{}) sql.Result {
489+
return MustInExec(db, query, args...)
490+
}
491+
369492
// Preparex returns an sqlx.Stmt instead of a sql.Stmt
370493
func (db *DB) Preparex(query string) (*Stmt, error) {
371494
return Preparex(db, query)
@@ -425,6 +548,39 @@ func (tx *Tx) NamedExec(query string, arg interface{}) (sql.Result, error) {
425548
return NamedExec(tx, query, arg)
426549
}
427550

551+
// In expands slice values in args, returning the modified query string
552+
// and a new arg list that can be executed by a database. The `query` should
553+
// use the `?` bindVar. The return value uses had rebinded bindvar type.
554+
func (tx *Tx) In(query string, args ...any) (string, []any, error) {
555+
q, params, err := In(query, args...)
556+
if err != nil {
557+
return "", nil, err
558+
}
559+
return tx.Rebind(q), params, nil
560+
}
561+
562+
// InExec executes a query that doesn't return rows for in.
563+
// For example: an INSERT and UPDATE.
564+
//
565+
// Exec uses context.Background internally; to specify the context, use
566+
// ExecContext.
567+
func (tx *Tx) InExec(query string, args ...any) (sql.Result, error) {
568+
return InExec(tx, query, args...)
569+
}
570+
571+
// InSelect within a transaction for in.
572+
// Any placeholder parameters are replaced with supplied args.
573+
func (tx *Tx) InSelect(dest any, query string, args ...any) error {
574+
return InSelect(tx, dest, query, args...)
575+
}
576+
577+
// Get within a transaction for in.
578+
// Any placeholder parameters are replaced with supplied args.
579+
// An error is returned if the result set is empty.
580+
func (tx *Tx) InGet(dest any, query string, args ...any) error {
581+
return InGet(tx, dest, query, args...)
582+
}
583+
428584
// Select within a transaction.
429585
// Any placeholder parameters are replaced with supplied args.
430586
func (tx *Tx) Select(dest interface{}, query string, args ...interface{}) error {
@@ -461,6 +617,12 @@ func (tx *Tx) MustExec(query string, args ...interface{}) sql.Result {
461617
return MustExec(tx, query, args...)
462618
}
463619

620+
// MustInExec runs MustExec within a transaction for in.
621+
// Any placeholder parameters are replaced with supplied args.
622+
func (tx *Tx) MustInExec(query string, args ...interface{}) sql.Result {
623+
return MustInExec(tx, query, args...)
624+
}
625+
464626
// Preparex a statement within a transaction.
465627
func (tx *Tx) Preparex(query string) (*Stmt, error) {
466628
return Preparex(tx, query)
@@ -690,6 +852,39 @@ func Get(q Queryer, dest interface{}, query string, args ...interface{}) error {
690852
return r.scanAny(dest, false)
691853
}
692854

855+
// InSelect for in scene executes a query using the provided Queryer, and StructScans each row
856+
// into dest, which must be a slice. If the slice elements are scannable, then
857+
// the result set must have only one column. Otherwise, StructScan is used.
858+
// The *sql.Rows are closed automatically.
859+
// Any placeholder parameters are replaced with supplied args.
860+
func InSelect(q QueryIn, dest interface{}, query string, args ...interface{}) error {
861+
newQuery, params, err := q.In(query, args...)
862+
if err != nil {
863+
return err
864+
}
865+
rows, err := q.Queryx(newQuery, params...)
866+
if err != nil {
867+
return err
868+
}
869+
// if something happens here, we want to make sure the rows are Closed
870+
defer rows.Close()
871+
return scanAll(rows, dest, false)
872+
}
873+
874+
// InGet for in scene does a QueryRow using the provided Queryer, and scans the resulting row
875+
// to dest. If dest is scannable, the result must only have one column. Otherwise,
876+
// StructScan is used. Get will return sql.ErrNoRows like row.Scan would.
877+
// Any placeholder parameters are replaced with supplied args.
878+
// An error is returned if the result set is empty.
879+
func InGet(q QueryIn, dest interface{}, query string, args ...interface{}) error {
880+
newQuery, params, err := q.In(query, args...)
881+
if err != nil {
882+
return err
883+
}
884+
r := q.QueryRowx(newQuery, params...)
885+
return r.scanAny(dest, false)
886+
}
887+
693888
// LoadFile exec's every statement in a file (as a single call to Exec).
694889
// LoadFile may return a nil *sql.Result if errors are encountered locating or
695890
// reading the file at path. LoadFile reads the entire file into memory, so it
@@ -724,6 +919,33 @@ func MustExec(e Execer, query string, args ...interface{}) sql.Result {
724919
return res
725920
}
726921

922+
// MustInExec for in scene execs the query using e and panics if there was an error.
923+
// Any placeholder parameters are replaced with supplied args.
924+
func MustInExec(e ExecIn, query string, args ...interface{}) sql.Result {
925+
newQuery, params, err := e.In(query, args...)
926+
if err != nil {
927+
panic(err)
928+
}
929+
res, err := e.Exec(newQuery, params...)
930+
if err != nil {
931+
panic(err)
932+
}
933+
return res
934+
}
935+
936+
// Exec for in scene executes a query that doesn't return rows.
937+
// For example: an INSERT and UPDATE.
938+
//
939+
// Exec uses context.Background internally; to specify the context, use
940+
// ExecContext.
941+
func InExec(e ExecIn, query string, args ...interface{}) (sql.Result, error) {
942+
newQuery, params, err := e.In(query, args...)
943+
if err != nil {
944+
return nil, err
945+
}
946+
return e.Exec(newQuery, params...)
947+
}
948+
727949
// SliceScan using this Rows.
728950
func (r *Row) SliceScan() ([]interface{}, error) {
729951
return SliceScan(r)

sqlx_context.go

+82
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,88 @@ func (c *Conn) BeginTxx(ctx context.Context, opts *sql.TxOptions) (*Tx, error) {
231231
return &Tx{Tx: tx, driverName: c.driverName, unsafe: c.unsafe, Mapper: c.Mapper}, err
232232
}
233233

234+
// With starts a transaction and do the give handle.
235+
//
236+
// The provided context is used until the transaction is committed or rolled back.
237+
// If the context is canceled, the sql package will roll back
238+
// the transaction. Tx.Commit will return an error if the context provided to
239+
// BeginTx is canceled.
240+
//
241+
// The provided TxOptions is optional and may be nil if defaults should be used.
242+
// If a non-default isolation level is used that the driver doesn't support,
243+
// an error will be returned.
244+
func (c *Conn) With(handle func(tx *sql.Tx) error) error {
245+
tx, err := c.BeginTx(context.Background(), nil)
246+
if err != nil {
247+
return err
248+
}
249+
defer tx.Rollback()
250+
if err = handle(tx); err != nil {
251+
return err
252+
}
253+
return tx.Commit()
254+
}
255+
256+
// WithTx starts a transaction and do the give handle.
257+
//
258+
// The provided context is used until the transaction is committed or rolled back.
259+
// If the context is canceled, the sql package will roll back
260+
// the transaction. Tx.Commit will return an error if the context provided to
261+
// BeginTx is canceled.
262+
//
263+
// The provided TxOptions is optional and may be nil if defaults should be used.
264+
// If a non-default isolation level is used that the driver doesn't support,
265+
// an error will be returned.
266+
func (c *Conn) WithTx(ctx context.Context, opts *sql.TxOptions, handle func(tx *sql.Tx) error) error {
267+
tx, err := c.BeginTx(ctx, opts)
268+
if err != nil {
269+
return err
270+
}
271+
defer tx.Rollback()
272+
if err = handle(tx); err != nil {
273+
return err
274+
}
275+
return tx.Commit()
276+
}
277+
278+
// Withx begins a transaction and returns an *sqlx.Tx instead of an
279+
// *sql.Tx and do the give handle.
280+
//
281+
// The provided context is used until the transaction is committed or rolled
282+
// back. If the context is canceled, the sql package will roll back the
283+
// transaction. Tx.Commit will return an error if the context provided to
284+
// BeginxContext is canceled.
285+
func (c *Conn) Withx(handle func(tx *Tx) error) error {
286+
tx, err := c.BeginTxx(context.Background(), nil)
287+
if err != nil {
288+
return err
289+
}
290+
defer tx.Rollback()
291+
if err = handle(tx); err != nil {
292+
return err
293+
}
294+
return tx.Commit()
295+
}
296+
297+
// WithTxx begins a transaction and returns an *sqlx.Tx instead of an
298+
// *sql.Tx and do the give handle.
299+
//
300+
// The provided context is used until the transaction is committed or rolled
301+
// back. If the context is canceled, the sql package will roll back the
302+
// transaction. Tx.Commit will return an error if the context provided to
303+
// BeginxContext is canceled.
304+
func (c *Conn) WithTxx(ctx context.Context, opts *sql.TxOptions, handle func(tx *Tx) error) error {
305+
tx, err := c.BeginTxx(ctx, opts)
306+
if err != nil {
307+
return err
308+
}
309+
defer tx.Rollback()
310+
if err = handle(tx); err != nil {
311+
return err
312+
}
313+
return tx.Commit()
314+
}
315+
234316
// SelectContext using this Conn.
235317
// Any placeholder parameters are replaced with supplied args.
236318
func (c *Conn) SelectContext(ctx context.Context, dest interface{}, query string, args ...interface{}) error {

0 commit comments

Comments
 (0)