git.sr.ht/~pingoo/stdx@v0.0.0-20240218134121-094174641f6e/db/sqlx.go (about)

     1  package db
     2  
     3  import (
     4  	"context"
     5  	"database/sql"
     6  	"fmt"
     7  	"runtime"
     8  	"time"
     9  
    10  	"github.com/jmoiron/sqlx"
    11  )
    12  
    13  // Database is wrapper of `sqlx.DB` which implements `DB`
    14  type Database struct {
    15  	sqlxDB *sqlx.DB
    16  }
    17  
    18  // Ping verifies a connection to the database is still alive, establishing a connection if necessary.
    19  func (db *Database) Ping(ctx context.Context) error {
    20  	return db.sqlxDB.PingContext(ctx)
    21  }
    22  
    23  // SetConnMaxLifetime sets the maximum amount of time a connection may be reused.
    24  func (db *Database) SetConnMaxLifetime(d time.Duration) {
    25  	db.sqlxDB.SetConnMaxLifetime(d)
    26  }
    27  
    28  // SetMaxIdleConns sets the maximum number of connections in the idle connection pool.
    29  func (db *Database) SetMaxIdleConns(n int) {
    30  	db.sqlxDB.SetMaxIdleConns(n)
    31  }
    32  
    33  // SetMaxOpenConns sets the maximum number of open connections to the database.
    34  func (db *Database) SetMaxOpenConns(n int) {
    35  	db.sqlxDB.SetMaxOpenConns(n)
    36  }
    37  
    38  // Stats returns database statistics.
    39  func (db *Database) Stats() sql.DBStats {
    40  	return db.sqlxDB.Stats()
    41  }
    42  
    43  // Begin starts a transaction. The default isolation level is dependent on the driver.
    44  // The provided context is used until the transaction is committed or rolled back. If the context is
    45  // canceled, the sql package will roll back the transaction. Tx.Commit will return an error if the
    46  // context provided to BeginTx is canceled.
    47  func (db *Database) Begin(ctx context.Context) (Tx, error) {
    48  	sqlxTx, err := db.sqlxDB.BeginTxx(ctx, nil)
    49  	return &Transaction{sqlxTx}, err
    50  }
    51  
    52  func (db *Database) Rebind(query string) (ret string) {
    53  	return db.sqlxDB.Rebind(query)
    54  }
    55  
    56  // BeginTx starts a transaction.
    57  //
    58  // The provided context is used until the transaction is committed or rolled back. If the context is
    59  // canceled, the sql package will roll back the transaction. Tx.Commit will return an error if the
    60  // context provided to BeginTx is canceled.
    61  //
    62  // The provided TxOptions is optional and may be nil if defaults should be used. If a non-default
    63  // isolation level is used that the driver doesn't support, an error will be returned.
    64  func (db *Database) BeginTx(ctx context.Context, opts *sql.TxOptions) (Tx, error) {
    65  	sqlxTx, err := db.sqlxDB.BeginTxx(ctx, opts)
    66  	return &Transaction{sqlxTx}, err
    67  }
    68  
    69  // Exec executes a query without returning any rows. The args are for any placeholder parameters in the query.
    70  func (db *Database) Exec(ctx context.Context, query string, args ...any) (sql.Result, error) {
    71  	return db.sqlxDB.ExecContext(ctx, query, args...)
    72  }
    73  
    74  // Get a single record. Any placeholder parameters are replaced with supplied args. An `ErrNoRows`
    75  // error is returned if the result set is empty.
    76  func (db *Database) Get(ctx context.Context, dest any, query string, args ...any) error {
    77  	return db.sqlxDB.GetContext(ctx, dest, query, args...)
    78  }
    79  
    80  // Query executes a query that returns rows, typically a SELECT. The args are for any placeholder
    81  // parameters in the query.
    82  func (db *Database) Query(ctx context.Context, query string, args ...any) (*sql.Rows, error) {
    83  	return db.sqlxDB.QueryContext(ctx, query, args...)
    84  }
    85  
    86  // Select an array of records. Any placeholder parameters are replaced with supplied args.
    87  func (db *Database) Select(ctx context.Context, dest any, query string, args ...any) error {
    88  	return db.sqlxDB.SelectContext(ctx, dest, query, args...)
    89  }
    90  
    91  func (db *Database) Transaction(ctx context.Context, fn func(tx Tx) error) (err error) {
    92  	sqlxTx, err := db.sqlxDB.BeginTxx(ctx, nil)
    93  	if err != nil {
    94  		return err
    95  	}
    96  
    97  	tx := &Transaction{sqlxTx}
    98  
    99  	defer func() {
   100  		if panicErr := recover(); panicErr != nil {
   101  			_, filename, line, _ := runtime.Caller(2)
   102  			rollbackErr := tx.Rollback()
   103  			if rollbackErr != nil {
   104  				err = fmt.Errorf("db: panic (%s:%d) in transaction + rollback: %v, %w", filename, line, panicErr, rollbackErr)
   105  			} else {
   106  				err = fmt.Errorf("db: panic (%s:%d) in transaction: %v", filename, line, panicErr)
   107  			}
   108  		}
   109  
   110  		if err == nil {
   111  			err = tx.Commit()
   112  		} else {
   113  			if rollbackErr := tx.Rollback(); rollbackErr != nil {
   114  				err = fmt.Errorf("db: transaction error: %w, rollback err: %w", err, rollbackErr)
   115  			}
   116  		}
   117  	}()
   118  
   119  	err = fn(tx)
   120  
   121  	return err
   122  }
   123  
   124  // Transaction is wrapper of `sqlx.Tx` which implements `Tx`
   125  type Transaction struct {
   126  	sqlxTx *sqlx.Tx
   127  }
   128  
   129  // Commit commits the transaction.
   130  func (tx *Transaction) Commit() error {
   131  	return tx.sqlxTx.Commit()
   132  }
   133  
   134  // Rollback aborts the transaction.
   135  func (tx *Transaction) Rollback() error {
   136  	return tx.sqlxTx.Rollback()
   137  }
   138  
   139  // Exec executes a query without returning any rows. The args are for any placeholder parameters in the query.
   140  func (tx *Transaction) Exec(ctx context.Context, query string, args ...any) (sql.Result, error) {
   141  	return tx.sqlxTx.ExecContext(ctx, query, args...)
   142  }
   143  
   144  // Get a single record. Any placeholder parameters are replaced with supplied args. An `ErrNoRows`
   145  // error is returned if the result set is empty.
   146  func (tx *Transaction) Get(ctx context.Context, dest any, query string, args ...any) error {
   147  	return tx.sqlxTx.GetContext(ctx, dest, query, args...)
   148  }
   149  
   150  // Query executes a query that returns rows, typically a SELECT. The args are for any placeholder
   151  // parameters in the query.
   152  func (tx *Transaction) Query(ctx context.Context, query string, args ...any) (*sql.Rows, error) {
   153  	return tx.sqlxTx.QueryContext(ctx, query, args...)
   154  }
   155  
   156  // Select an array of records. Any placeholder parameters are replaced with supplied args.
   157  func (tx *Transaction) Select(ctx context.Context, dest any, query string, args ...any) error {
   158  	return tx.sqlxTx.SelectContext(ctx, dest, query, args...)
   159  }
   160  
   161  func (tx *Transaction) Rebind(query string) (ret string) {
   162  	return tx.sqlxTx.Rebind(query)
   163  }