mirror of https://github.com/jackc/pgx.git
Allow recovery from failed transaction
rollback to savepoint can recover a failed transaction. Therefore we shouldn't block any activities while the transaction is broken. Instead we only have the Tx.Status() method return the information. refs #421issue421
parent
e096a14b3e
commit
f114ec85a1
24
tx.go
24
tx.go
|
@ -179,10 +179,7 @@ func (tx *Tx) Exec(sql string, arguments ...interface{}) (commandTag CommandTag,
|
|||
|
||||
// ExecEx delegates to the underlying *Conn
|
||||
func (tx *Tx) ExecEx(ctx context.Context, sql string, options *QueryExOptions, arguments ...interface{}) (commandTag CommandTag, err error) {
|
||||
if tx.Status() == TxStatusInFailure {
|
||||
return CommandTag(""), ErrTxInFailure
|
||||
}
|
||||
if tx.Status() != TxStatusInProgress {
|
||||
if tx.status != TxStatusInProgress {
|
||||
return CommandTag(""), ErrTxClosed
|
||||
}
|
||||
|
||||
|
@ -196,10 +193,7 @@ func (tx *Tx) Prepare(name, sql string) (*PreparedStatement, error) {
|
|||
|
||||
// PrepareEx delegates to the underlying *Conn
|
||||
func (tx *Tx) PrepareEx(ctx context.Context, name, sql string, opts *PrepareExOptions) (*PreparedStatement, error) {
|
||||
if tx.Status() == TxStatusInFailure {
|
||||
return nil, ErrTxInFailure
|
||||
}
|
||||
if tx.Status() != TxStatusInProgress {
|
||||
if tx.status != TxStatusInProgress {
|
||||
return nil, ErrTxClosed
|
||||
}
|
||||
|
||||
|
@ -213,12 +207,7 @@ func (tx *Tx) Query(sql string, args ...interface{}) (*Rows, error) {
|
|||
|
||||
// QueryEx delegates to the underlying *Conn
|
||||
func (tx *Tx) QueryEx(ctx context.Context, sql string, options *QueryExOptions, args ...interface{}) (*Rows, error) {
|
||||
if tx.Status() == TxStatusInFailure {
|
||||
// Because checking for errors can be deferred to the *Rows, build one with the error
|
||||
err := ErrTxInFailure
|
||||
return &Rows{closed: true, err: err}, err
|
||||
}
|
||||
if tx.Status() != TxStatusInProgress {
|
||||
if tx.status != TxStatusInProgress {
|
||||
// Because checking for errors can be deferred to the *Rows, build one with the error
|
||||
err := ErrTxClosed
|
||||
return &Rows{closed: true, err: err}, err
|
||||
|
@ -241,10 +230,7 @@ func (tx *Tx) QueryRowEx(ctx context.Context, sql string, options *QueryExOption
|
|||
|
||||
// CopyFrom delegates to the underlying *Conn
|
||||
func (tx *Tx) CopyFrom(tableName Identifier, columnNames []string, rowSrc CopyFromSource) (int, error) {
|
||||
if tx.Status() == TxStatusInFailure {
|
||||
return 0, ErrTxInFailure
|
||||
}
|
||||
if tx.Status() != TxStatusInProgress {
|
||||
if tx.status != TxStatusInProgress {
|
||||
return 0, ErrTxClosed
|
||||
}
|
||||
|
||||
|
@ -255,7 +241,7 @@ func (tx *Tx) CopyFrom(tableName Identifier, columnNames []string, rowSrc CopyFr
|
|||
// pgx.TxStatus* constants.
|
||||
func (tx *Tx) Status() int8 {
|
||||
if tx.status == TxStatusInProgress && tx.conn.txStatus == 'E' {
|
||||
tx.status = TxStatusInFailure
|
||||
return TxStatusInFailure
|
||||
}
|
||||
return tx.status
|
||||
}
|
||||
|
|
14
tx_test.go
14
tx_test.go
|
@ -369,6 +369,11 @@ func TestTxStatusErrorInTransactions(t *testing.T) {
|
|||
t.Fatalf("Expected status to be %v, but it was %v", pgx.TxStatusInProgress, status)
|
||||
}
|
||||
|
||||
_, err = tx.Exec("savepoint s")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = tx.Exec("syntax error")
|
||||
if err == nil {
|
||||
t.Fatal("expected an error but did not get one")
|
||||
|
@ -378,6 +383,15 @@ func TestTxStatusErrorInTransactions(t *testing.T) {
|
|||
t.Fatalf("Expected status to be %v, but it was %v", pgx.TxStatusInFailure, status)
|
||||
}
|
||||
|
||||
_, err = tx.Exec("rollback to s")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if status := tx.Status(); status != pgx.TxStatusInProgress {
|
||||
t.Fatalf("Expected status to be %v, but it was %v", pgx.TxStatusInProgress, status)
|
||||
}
|
||||
|
||||
if err := tx.Rollback(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue