goext/sq/transaction.go

184 lines
4.6 KiB
Go
Raw Normal View History

2022-12-07 23:21:36 +01:00
package sq
import (
"context"
"database/sql"
"github.com/jmoiron/sqlx"
2024-01-13 14:19:19 +01:00
"gogs.mikescher.com/BlackForestBytes/goext/exerr"
2022-12-07 23:21:36 +01:00
"gogs.mikescher.com/BlackForestBytes/goext/langext"
"time"
2022-12-07 23:21:36 +01:00
)
2023-07-27 17:12:41 +02:00
type TxStatus string
const (
TxStatusInitial TxStatus = "INITIAL"
TxStatusActive TxStatus = "ACTIVE"
TxStatusComitted TxStatus = "COMMITTED"
TxStatusRollback TxStatus = "ROLLBACK"
)
2022-12-07 23:21:36 +01:00
type Tx interface {
2023-12-29 19:25:36 +01:00
Queryable
2022-12-07 23:21:36 +01:00
Rollback() error
Commit() error
2023-07-27 17:12:41 +02:00
Status() TxStatus
2022-12-07 23:21:36 +01:00
}
type transaction struct {
2024-10-05 00:58:15 +02:00
constructorContext context.Context
tx *sqlx.Tx
id uint16
status TxStatus
execCtr int
queryCtr int
db *database
2022-12-07 23:21:36 +01:00
}
2024-10-05 00:58:15 +02:00
func newTransaction(ctx context.Context, xtx *sqlx.Tx, txid uint16, db *database) Tx {
2022-12-07 23:21:36 +01:00
return &transaction{
2024-10-05 00:58:15 +02:00
constructorContext: ctx,
tx: xtx,
id: txid,
status: TxStatusInitial,
execCtr: 0,
queryCtr: 0,
db: db,
2022-12-07 23:21:36 +01:00
}
}
func (tx *transaction) Rollback() error {
t0 := time.Now()
2024-10-05 00:58:15 +02:00
preMeta := PreTxRollbackMeta{ConstructorContext: tx.constructorContext}
2023-12-29 19:25:36 +01:00
for _, v := range tx.db.lstr {
err := v.PreTxRollback(tx.id, preMeta)
2022-12-21 15:34:59 +01:00
if err != nil {
2024-01-13 14:19:19 +01:00
return exerr.Wrap(err, "failed to call SQL pre-rollback listener").Int("tx.id", int(tx.id)).Build()
2022-12-21 15:34:59 +01:00
}
2022-12-07 23:21:36 +01:00
}
t1 := time.Now()
2022-12-21 15:34:59 +01:00
result := tx.tx.Rollback()
2023-07-27 17:16:30 +02:00
if result == nil {
2023-07-27 17:12:41 +02:00
tx.status = TxStatusRollback
}
2024-10-05 00:58:15 +02:00
postMeta := PostTxRollbackMeta{ConstructorContext: tx.constructorContext, Init: t0, Start: t1, End: time.Now(), ExecCounter: tx.execCtr, QueryCounter: tx.queryCtr}
2023-12-29 19:25:36 +01:00
for _, v := range tx.db.lstr {
v.PostTxRollback(tx.id, result, postMeta)
2022-12-21 15:34:59 +01:00
}
return result
2022-12-07 23:21:36 +01:00
}
func (tx *transaction) Commit() error {
t0 := time.Now()
2024-10-05 00:58:15 +02:00
preMeta := PreTxCommitMeta{ConstructorContext: tx.constructorContext}
2023-12-29 19:25:36 +01:00
for _, v := range tx.db.lstr {
err := v.PreTxCommit(tx.id, preMeta)
2022-12-21 15:34:59 +01:00
if err != nil {
2024-01-13 14:19:19 +01:00
return exerr.Wrap(err, "failed to call SQL pre-commit listener").Int("tx.id", int(tx.id)).Build()
2022-12-21 15:34:59 +01:00
}
}
t1 := time.Now()
2022-12-21 15:34:59 +01:00
result := tx.tx.Commit()
2023-07-27 17:16:30 +02:00
if result == nil {
2023-07-27 17:12:41 +02:00
tx.status = TxStatusComitted
}
2024-10-05 00:58:15 +02:00
postMeta := PostTxCommitMeta{ConstructorContext: tx.constructorContext, Init: t0, Start: t1, End: time.Now(), ExecCounter: tx.execCtr, QueryCounter: tx.queryCtr}
2023-12-29 19:25:36 +01:00
for _, v := range tx.db.lstr {
v.PostTxCommit(tx.id, result, postMeta)
2022-12-07 23:21:36 +01:00
}
2022-12-21 15:34:59 +01:00
return result
2022-12-07 23:21:36 +01:00
}
2022-12-21 15:34:59 +01:00
func (tx *transaction) Exec(ctx context.Context, sqlstr string, prep PP) (sql.Result, error) {
origsql := sqlstr
t0 := time.Now()
preMeta := PreExecMeta{}
2023-12-29 19:25:36 +01:00
for _, v := range tx.db.lstr {
err := v.PreExec(ctx, langext.Ptr(tx.id), &sqlstr, &prep, preMeta)
2022-12-21 15:34:59 +01:00
if err != nil {
2024-01-13 14:19:19 +01:00
return nil, exerr.Wrap(err, "failed to call SQL pre-exec listener").Int("tx.id", int(tx.id)).Str("original_sql", origsql).Str("sql", sqlstr).Any("sql_params", prep).Build()
2022-12-21 15:34:59 +01:00
}
}
t1 := time.Now()
2022-12-21 15:34:59 +01:00
res, err := tx.tx.NamedExecContext(ctx, sqlstr, prep)
tx.execCtr++
2022-12-21 15:34:59 +01:00
2023-07-27 17:16:30 +02:00
if tx.status == TxStatusInitial && err == nil {
2023-07-27 17:12:41 +02:00
tx.status = TxStatusActive
}
postMeta := PostExecMeta{Init: t0, Start: t1, End: time.Now()}
2023-12-29 19:25:36 +01:00
for _, v := range tx.db.lstr {
v.PostExec(langext.Ptr(tx.id), origsql, sqlstr, prep, postMeta)
2022-12-07 23:21:36 +01:00
}
if err != nil {
2024-01-13 14:19:19 +01:00
return nil, exerr.Wrap(err, "Failed to [exec] sql statement").Int("tx.id", int(tx.id)).Str("original_sql", origsql).Str("sql", sqlstr).Any("sql_params", prep).Build()
2022-12-07 23:21:36 +01:00
}
return res, nil
}
2022-12-21 15:34:59 +01:00
func (tx *transaction) Query(ctx context.Context, sqlstr string, prep PP) (*sqlx.Rows, error) {
origsql := sqlstr
t0 := time.Now()
preMeta := PreQueryMeta{}
2023-12-29 19:25:36 +01:00
for _, v := range tx.db.lstr {
err := v.PreQuery(ctx, langext.Ptr(tx.id), &sqlstr, &prep, preMeta)
2022-12-21 15:34:59 +01:00
if err != nil {
2024-01-13 14:19:19 +01:00
return nil, exerr.Wrap(err, "failed to call SQL pre-query listener").Int("tx.id", int(tx.id)).Str("original_sql", origsql).Str("sql", sqlstr).Any("sql_params", prep).Build()
2022-12-21 15:34:59 +01:00
}
}
t1 := time.Now()
2022-12-21 15:34:59 +01:00
rows, err := sqlx.NamedQueryContext(ctx, tx.tx, sqlstr, prep)
tx.queryCtr++
2022-12-21 15:34:59 +01:00
2023-07-27 17:16:30 +02:00
if tx.status == TxStatusInitial && err == nil {
2023-07-27 17:12:41 +02:00
tx.status = TxStatusActive
}
postMeta := PostQueryMeta{Init: t0, Start: t1, End: time.Now()}
2023-12-29 19:25:36 +01:00
for _, v := range tx.db.lstr {
v.PostQuery(langext.Ptr(tx.id), origsql, sqlstr, prep, postMeta)
2022-12-07 23:21:36 +01:00
}
if err != nil {
2024-01-13 14:19:19 +01:00
return nil, exerr.Wrap(err, "Failed to [query] sql statement").Int("tx.id", int(tx.id)).Str("original_sql", origsql).Str("sql", sqlstr).Any("sql_params", prep).Build()
2022-12-07 23:21:36 +01:00
}
return rows, nil
}
2023-07-27 17:12:41 +02:00
func (tx *transaction) Status() TxStatus {
return tx.status
}
2023-12-29 19:25:36 +01:00
func (tx *transaction) ListConverter() []DBTypeConverter {
return tx.db.conv
}
2023-07-27 17:12:41 +02:00
func (tx *transaction) Traffic() (int, int) {
return tx.execCtr, tx.queryCtr
}