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 }