diff options
Diffstat (limited to 'vendor/github.com/jackc/pgx/tx.go')
| -rw-r--r-- | vendor/github.com/jackc/pgx/tx.go | 207 |
1 files changed, 207 insertions, 0 deletions
diff --git a/vendor/github.com/jackc/pgx/tx.go b/vendor/github.com/jackc/pgx/tx.go new file mode 100644 index 0000000..deb6c01 --- /dev/null +++ b/vendor/github.com/jackc/pgx/tx.go @@ -0,0 +1,207 @@ +package pgx + +import ( + "errors" + "fmt" +) + +// Transaction isolation levels +const ( + Serializable = "serializable" + RepeatableRead = "repeatable read" + ReadCommitted = "read committed" + ReadUncommitted = "read uncommitted" +) + +const ( + TxStatusInProgress = 0 + TxStatusCommitFailure = -1 + TxStatusRollbackFailure = -2 + TxStatusCommitSuccess = 1 + TxStatusRollbackSuccess = 2 +) + +var ErrTxClosed = errors.New("tx is closed") + +// ErrTxCommitRollback occurs when an error has occurred in a transaction and +// Commit() is called. PostgreSQL accepts COMMIT on aborted transactions, but +// it is treated as ROLLBACK. +var ErrTxCommitRollback = errors.New("commit unexpectedly resulted in rollback") + +// Begin starts a transaction with the default isolation level for the current +// connection. To use a specific isolation level see BeginIso. +func (c *Conn) Begin() (*Tx, error) { + return c.begin("") +} + +// BeginIso starts a transaction with isoLevel as the transaction isolation +// level. +// +// Valid isolation levels (and their constants) are: +// serializable (pgx.Serializable) +// repeatable read (pgx.RepeatableRead) +// read committed (pgx.ReadCommitted) +// read uncommitted (pgx.ReadUncommitted) +func (c *Conn) BeginIso(isoLevel string) (*Tx, error) { + return c.begin(isoLevel) +} + +func (c *Conn) begin(isoLevel string) (*Tx, error) { + var beginSQL string + if isoLevel == "" { + beginSQL = "begin" + } else { + beginSQL = fmt.Sprintf("begin isolation level %s", isoLevel) + } + + _, err := c.Exec(beginSQL) + if err != nil { + return nil, err + } + + return &Tx{conn: c}, nil +} + +// Tx represents a database transaction. +// +// All Tx methods return ErrTxClosed if Commit or Rollback has already been +// called on the Tx. +type Tx struct { + conn *Conn + afterClose func(*Tx) + err error + status int8 +} + +// Commit commits the transaction +func (tx *Tx) Commit() error { + if tx.status != TxStatusInProgress { + return ErrTxClosed + } + + commandTag, err := tx.conn.Exec("commit") + if err == nil && commandTag == "COMMIT" { + tx.status = TxStatusCommitSuccess + } else if err == nil && commandTag == "ROLLBACK" { + tx.status = TxStatusCommitFailure + tx.err = ErrTxCommitRollback + } else { + tx.status = TxStatusCommitFailure + tx.err = err + } + + if tx.afterClose != nil { + tx.afterClose(tx) + } + return tx.err +} + +// Rollback rolls back the transaction. Rollback will return ErrTxClosed if the +// Tx is already closed, but is otherwise safe to call multiple times. Hence, a +// defer tx.Rollback() is safe even if tx.Commit() will be called first in a +// non-error condition. +func (tx *Tx) Rollback() error { + if tx.status != TxStatusInProgress { + return ErrTxClosed + } + + _, tx.err = tx.conn.Exec("rollback") + if tx.err == nil { + tx.status = TxStatusRollbackSuccess + } else { + tx.status = TxStatusRollbackFailure + } + + if tx.afterClose != nil { + tx.afterClose(tx) + } + return tx.err +} + +// Exec delegates to the underlying *Conn +func (tx *Tx) Exec(sql string, arguments ...interface{}) (commandTag CommandTag, err error) { + if tx.status != TxStatusInProgress { + return CommandTag(""), ErrTxClosed + } + + return tx.conn.Exec(sql, arguments...) +} + +// Prepare delegates to the underlying *Conn +func (tx *Tx) Prepare(name, sql string) (*PreparedStatement, error) { + return tx.PrepareEx(name, sql, nil) +} + +// PrepareEx delegates to the underlying *Conn +func (tx *Tx) PrepareEx(name, sql string, opts *PrepareExOptions) (*PreparedStatement, error) { + if tx.status != TxStatusInProgress { + return nil, ErrTxClosed + } + + return tx.conn.PrepareEx(name, sql, opts) +} + +// Query delegates to the underlying *Conn +func (tx *Tx) Query(sql string, args ...interface{}) (*Rows, error) { + 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 + } + + return tx.conn.Query(sql, args...) +} + +// QueryRow delegates to the underlying *Conn +func (tx *Tx) QueryRow(sql string, args ...interface{}) *Row { + rows, _ := tx.Query(sql, args...) + return (*Row)(rows) +} + +// Deprecated. Use CopyFrom instead. CopyTo delegates to the underlying *Conn +func (tx *Tx) CopyTo(tableName string, columnNames []string, rowSrc CopyToSource) (int, error) { + if tx.status != TxStatusInProgress { + return 0, ErrTxClosed + } + + return tx.conn.CopyTo(tableName, columnNames, rowSrc) +} + +// CopyFrom delegates to the underlying *Conn +func (tx *Tx) CopyFrom(tableName Identifier, columnNames []string, rowSrc CopyFromSource) (int, error) { + if tx.status != TxStatusInProgress { + return 0, ErrTxClosed + } + + return tx.conn.CopyFrom(tableName, columnNames, rowSrc) +} + +// Conn returns the *Conn this transaction is using. +func (tx *Tx) Conn() *Conn { + return tx.conn +} + +// Status returns the status of the transaction from the set of +// pgx.TxStatus* constants. +func (tx *Tx) Status() int8 { + return tx.status +} + +// Err returns the final error state, if any, of calling Commit or Rollback. +func (tx *Tx) Err() error { + return tx.err +} + +// AfterClose adds f to a LILO queue of functions that will be called when +// the transaction is closed (either Commit or Rollback). +func (tx *Tx) AfterClose(f func(*Tx)) { + if tx.afterClose == nil { + tx.afterClose = f + } else { + prevFn := tx.afterClose + tx.afterClose = func(tx *Tx) { + f(tx) + prevFn(tx) + } + } +} |
