package pg

import (
	
	
	
	
	

	
	
	
)

// ErrTxDone is returned by any operation that is performed on a transaction
// that has already been committed or rolled back.
var ErrTxDone = errors.New("pg: transaction has already been committed or rolled back")

// Tx is an in-progress database transaction. It is safe for concurrent use
// by multiple goroutines.
//
// A transaction must end with a call to Commit or Rollback.
//
// After a call to Commit or Rollback, all operations on the transaction fail
// with ErrTxDone.
//
// The statements prepared for a transaction by calling the transaction's
// Prepare or Stmt methods are closed by the call to Commit or Rollback.
type Tx struct {
	db  *baseDB
	ctx context.Context

	stmtsMu sync.Mutex
	stmts   []*Stmt

	_closed int32
}

var _ orm.DB = (*Tx)(nil)

// Context returns the context.Context of the transaction.
func ( *Tx) () context.Context {
	return .ctx
}

// Begin starts a transaction. Most callers should use RunInTransaction instead.
func ( *baseDB) () (*Tx, error) {
	return .BeginContext(.db.Context())
}

func ( *baseDB) ( context.Context) (*Tx, error) {
	 := &Tx{
		db:  .withPool(pool.NewStickyConnPool(.pool)),
		ctx: ,
	}

	 := .begin()
	if  != nil {
		.close()
		return nil, 
	}

	return , nil
}

// RunInTransaction runs a function in a transaction. If function
// returns an error transaction is rolled back, otherwise transaction
// is committed.
func ( *baseDB) ( context.Context,  func(*Tx) error) error {
	,  := .BeginContext()
	if  != nil {
		return 
	}
	return .RunInTransaction(, )
}

// Begin returns current transaction. It does not start new transaction.
func ( *Tx) () (*Tx, error) {
	return , nil
}

// RunInTransaction runs a function in the transaction. If function
// returns an error transaction is rolled back, otherwise transaction
// is committed.
func ( *Tx) ( context.Context,  func(*Tx) error) error {
	defer func() {
		if  := recover();  != nil {
			if  := .RollbackContext();  != nil {
				internal.Logger.Printf(, "tx.Rollback panicked: %s", )
			}
			panic()
		}
	}()

	if  := ();  != nil {
		if  := .RollbackContext();  != nil {
			internal.Logger.Printf(, "tx.Rollback failed: %s", )
		}
		return 
	}
	return .CommitContext()
}

func ( *Tx) ( context.Context,  func(context.Context, *pool.Conn) error) error {
	 := .db.withConn(, )
	if .closed() &&  == pool.ErrClosed {
		return ErrTxDone
	}
	return 
}

// Stmt returns a transaction-specific prepared statement
// from an existing statement.
func ( *Tx) ( *Stmt) *Stmt {
	,  := .Prepare(.q)
	if  != nil {
		return &Stmt{stickyErr: }
	}
	return 
}

// Prepare creates a prepared statement for use within a transaction.
//
// The returned statement operates within the transaction and can no longer
// be used once the transaction has been committed or rolled back.
//
// To use an existing prepared statement on this transaction, see Tx.Stmt.
func ( *Tx) ( string) (*Stmt, error) {
	.stmtsMu.Lock()
	defer .stmtsMu.Unlock()

	 := .db.withPool(pool.NewStickyConnPool(.db.pool))
	,  := prepareStmt(, )
	if  != nil {
		return nil, 
	}
	.stmts = append(.stmts, )

	return , nil
}

// Exec is an alias for DB.Exec.
func ( *Tx) ( interface{},  ...interface{}) (Result, error) {
	return .exec(.ctx, , ...)
}

// ExecContext acts like Exec but additionally receives a context.
func ( *Tx) ( context.Context,  interface{},  ...interface{}) (Result, error) {
	return .exec(, , ...)
}

func ( *Tx) ( context.Context,  interface{},  ...interface{}) (Result, error) {
	 := pool.GetWriteBuffer()
	defer pool.PutWriteBuffer()

	if  := writeQueryMsg(, .db.fmter, , ...);  != nil {
		return nil, 
	}

	, ,  := .db.beforeQuery(, , nil, , , .Query())
	if  != nil {
		return nil, 
	}

	var  Result
	 := .withConn(, func( context.Context,  *pool.Conn) error {
		,  = .db.simpleQuery(, , )
		return 
	})

	if  := .db.afterQuery(, , , );  != nil {
		return nil, 
	}
	return , 
}

// ExecOne is an alias for DB.ExecOne.
func ( *Tx) ( interface{},  ...interface{}) (Result, error) {
	return .execOne(.ctx, , ...)
}

// ExecOneContext acts like ExecOne but additionally receives a context.
func ( *Tx) ( context.Context,  interface{},  ...interface{}) (Result, error) {
	return .execOne(, , ...)
}

func ( *Tx) ( context.Context,  interface{},  ...interface{}) (Result, error) {
	,  := .ExecContext(, , ...)
	if  != nil {
		return nil, 
	}

	if  := internal.AssertOneRow(.RowsAffected());  != nil {
		return nil, 
	}
	return , nil
}

// Query is an alias for DB.Query.
func ( *Tx) ( interface{},  interface{},  ...interface{}) (Result, error) {
	return .query(.ctx, , , ...)
}

// QueryContext acts like Query but additionally receives a context.
func ( *Tx) (
	 context.Context,
	 interface{},
	 interface{},
	 ...interface{},
) (Result, error) {
	return .query(, , , ...)
}

func ( *Tx) (
	 context.Context,
	 interface{},
	 interface{},
	 ...interface{},
) (Result, error) {
	 := pool.GetWriteBuffer()
	defer pool.PutWriteBuffer()

	if  := writeQueryMsg(, .db.fmter, , ...);  != nil {
		return nil, 
	}

	, ,  := .db.beforeQuery(, , , , , .Query())
	if  != nil {
		return nil, 
	}

	var  *result
	 := .withConn(, func( context.Context,  *pool.Conn) error {
		,  = .db.simpleQueryData(, , , )
		return 
	})

	if  := .db.afterQuery(, , , );  != nil {
		return nil, 
	}
	return , 
}

// QueryOne is an alias for DB.QueryOne.
func ( *Tx) ( interface{},  interface{},  ...interface{}) (Result, error) {
	return .queryOne(.ctx, , , ...)
}

// QueryOneContext acts like QueryOne but additionally receives a context.
func ( *Tx) (
	 context.Context,
	 interface{},
	 interface{},
	 ...interface{},
) (Result, error) {
	return .queryOne(, , , ...)
}

func ( *Tx) (
	 context.Context,
	 interface{},
	 interface{},
	 ...interface{},
) (Result, error) {
	,  := orm.NewModel()
	if  != nil {
		return nil, 
	}

	,  := .QueryContext(, , , ...)
	if  != nil {
		return nil, 
	}

	if  := internal.AssertOneRow(.RowsAffected());  != nil {
		return nil, 
	}
	return , nil
}

// Model is an alias for DB.Model.
func ( *Tx) ( ...interface{}) *Query {
	return orm.NewQuery(, ...)
}

// ModelContext acts like Model but additionally receives a context.
func ( *Tx) ( context.Context,  ...interface{}) *Query {
	return orm.NewQueryContext(, , ...)
}

// CopyFrom is an alias for DB.CopyFrom.
func ( *Tx) ( io.Reader,  interface{},  ...interface{}) ( Result,  error) {
	 = .withConn(.ctx, func( context.Context,  *pool.Conn) error {
		,  = .db.copyFrom(, , , , ...)
		return 
	})
	return , 
}

// CopyTo is an alias for DB.CopyTo.
func ( *Tx) ( io.Writer,  interface{},  ...interface{}) ( Result,  error) {
	 = .withConn(.ctx, func( context.Context,  *pool.Conn) error {
		,  = .db.copyTo(, , , , ...)
		return 
	})
	return , 
}

// Formatter is an alias for DB.Formatter.
func ( *Tx) () orm.QueryFormatter {
	return .db.Formatter()
}

func ( *Tx) ( context.Context) error {
	var  error
	for  := 0;  <= .db.opt.MaxRetries; ++ {
		if  > 0 {
			if  := internal.Sleep(, .db.retryBackoff(-1));  != nil {
				return 
			}

			 := .db.pool.(*pool.StickyConnPool).Reset()
			if  != nil {
				return 
			}
		}

		_,  = .ExecContext(, "BEGIN")
		if !.db.shouldRetry() {
			break
		}
	}
	return 
}

func ( *Tx) () error {
	return .CommitContext(.ctx)
}

// Commit commits the transaction.
func ( *Tx) ( context.Context) error {
	,  := .ExecContext(internal.UndoContext(), "COMMIT")
	.close()
	return 
}

func ( *Tx) () error {
	return .RollbackContext(.ctx)
}

// Rollback aborts the transaction.
func ( *Tx) ( context.Context) error {
	,  := .ExecContext(internal.UndoContext(), "ROLLBACK")
	.close()
	return 
}

func ( *Tx) () error {
	return .CloseContext(.ctx)
}

// Close calls Rollback if the tx has not already been committed or rolled back.
func ( *Tx) ( context.Context) error {
	if .closed() {
		return nil
	}
	return .RollbackContext()
}

func ( *Tx) () {
	if !atomic.CompareAndSwapInt32(&._closed, 0, 1) {
		return
	}

	.stmtsMu.Lock()
	defer .stmtsMu.Unlock()

	for ,  := range .stmts {
		_ = .Close()
	}
	.stmts = nil

	_ = .db.Close()
}

func ( *Tx) () bool {
	return atomic.LoadInt32(&._closed) == 1
}