From f114ec85a1f3c00a541d34f7fa2af9e16ec674e0 Mon Sep 17 00:00:00 2001 From: Jack Christensen Date: Sat, 12 May 2018 19:53:53 -0500 Subject: [PATCH] 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 #421 --- tx.go | 24 +++++------------------- tx_test.go | 14 ++++++++++++++ 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/tx.go b/tx.go index a1e67766..1fe9d72a 100644 --- a/tx.go +++ b/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 } diff --git a/tx_test.go b/tx_test.go index 8c562b7e..f9a9d5c7 100644 --- a/tx_test.go +++ b/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) }