package sqlite3

import (
	
	
	
	
	

	

	
)

// Txn is an in-progress database transaction.
//
// https://sqlite.org/lang_transaction.html
type Txn struct {
	c *Conn
}

// Begin starts a deferred transaction.
// It panics if a transaction is in-progress.
// For nested transactions, use [Conn.Savepoint].
//
// https://sqlite.org/lang_transaction.html
func ( *Conn) () Txn {
	// BEGIN even if interrupted.
	 := .exec(`BEGIN DEFERRED`)
	if  != nil {
		panic()
	}
	return Txn{}
}

// BeginConcurrent starts a concurrent transaction.
//
// Experimental: requires a custom build of SQLite.
//
// https://sqlite.org/cgi/src/doc/begin-concurrent/doc/begin_concurrent.md
func ( *Conn) () (Txn, error) {
	 := .Exec(`BEGIN CONCURRENT`)
	if  != nil {
		return Txn{}, 
	}
	return Txn{}, nil
}

// BeginImmediate starts an immediate transaction.
//
// https://sqlite.org/lang_transaction.html
func ( *Conn) () (Txn, error) {
	 := .Exec(`BEGIN IMMEDIATE`)
	if  != nil {
		return Txn{}, 
	}
	return Txn{}, nil
}

// BeginExclusive starts an exclusive transaction.
//
// https://sqlite.org/lang_transaction.html
func ( *Conn) () (Txn, error) {
	 := .Exec(`BEGIN EXCLUSIVE`)
	if  != nil {
		return Txn{}, 
	}
	return Txn{}, nil
}

// End calls either [Txn.Commit] or [Txn.Rollback]
// depending on whether *error points to a nil or non-nil error.
//
// This is meant to be deferred:
//
//	func doWork(db *sqlite3.Conn) (err error) {
//		tx := db.Begin()
//		defer tx.End(&err)
//
//		// ... do work in the transaction
//	}
//
// https://sqlite.org/lang_transaction.html
func ( Txn) ( *error) {
	 := recover()
	if  != nil {
		defer panic()
	}

	if * == nil &&  == nil {
		// Success path.
		if .c.GetAutocommit() { // There is nothing to commit.
			return
		}
		* = .Commit()
		if * == nil {
			return
		}
		// Fall through to the error path.
	}

	// Error path.
	if .c.GetAutocommit() { // There is nothing to rollback.
		return
	}
	 := .Rollback()
	if  != nil {
		panic()
	}
}

// Commit commits the transaction.
//
// https://sqlite.org/lang_transaction.html
func ( Txn) () error {
	return .c.Exec(`COMMIT`)
}

// Rollback rolls back the transaction,
// even if the connection has been interrupted.
//
// https://sqlite.org/lang_transaction.html
func ( Txn) () error {
	// ROLLBACK even if interrupted.
	return .c.exec(`ROLLBACK`)
}

// Savepoint is a marker within a transaction
// that allows for partial rollback.
//
// https://sqlite.org/lang_savepoint.html
type Savepoint struct {
	c    *Conn
	name string
}

// Savepoint establishes a new transaction savepoint.
//
// https://sqlite.org/lang_savepoint.html
func ( *Conn) () Savepoint {
	 := callerName()
	if  == "" {
		 = "sqlite3.Savepoint"
	}
	// Names can be reused, but this makes catching bugs more likely.
	 = QuoteIdentifier( + "_" + strconv.Itoa(int(rand.Int31())))

	 := .exec(`SAVEPOINT ` + )
	if  != nil {
		panic()
	}
	return Savepoint{c: , name: }
}

func callerName() ( string) {
	var  [8]uintptr
	 := runtime.Callers(3, [:])
	if  <= 0 {
		return ""
	}
	 := runtime.CallersFrames([:])
	,  := .Next()
	for  && (strings.HasPrefix(.Function, "database/sql.") ||
		strings.HasPrefix(.Function, "github.com/ncruces/go-sqlite3/driver.")) {
		,  = .Next()
	}
	return .Function
}

// Release releases the savepoint rolling back any changes
// if *error points to a non-nil error.
//
// This is meant to be deferred:
//
//	func doWork(db *sqlite3.Conn) (err error) {
//		savept := db.Savepoint()
//		defer savept.Release(&err)
//
//		// ... do work in the transaction
//	}
func ( Savepoint) ( *error) {
	 := recover()
	if  != nil {
		defer panic()
	}

	if * == nil &&  == nil {
		// Success path.
		if .c.GetAutocommit() { // There is nothing to commit.
			return
		}
		* = .c.Exec(`RELEASE ` + .name)
		if * == nil {
			return
		}
		// Fall through to the error path.
	}

	// Error path.
	if .c.GetAutocommit() { // There is nothing to rollback.
		return
	}
	// ROLLBACK and RELEASE even if interrupted.
	 := .c.exec(`ROLLBACK TO ` + .name + `; RELEASE ` + .name)
	if  != nil {
		panic()
	}
}

// Rollback rolls the transaction back to the savepoint,
// even if the connection has been interrupted.
// Rollback does not release the savepoint.
//
// https://sqlite.org/lang_transaction.html
func ( Savepoint) () error {
	// ROLLBACK even if interrupted.
	return .c.exec(`ROLLBACK TO ` + .name)
}

// TxnState determines the transaction state of a database.
//
// https://sqlite.org/c3ref/txn_state.html
func ( *Conn) ( string) TxnState {
	var  ptr_t
	if  != "" {
		defer .arena.mark()()
		 = .arena.string()
	}
	return TxnState(.call("sqlite3_txn_state", stk_t(.handle), stk_t()))
}

// CommitHook registers a callback function to be invoked
// whenever a transaction is committed.
// Return true to allow the commit operation to continue normally.
//
// https://sqlite.org/c3ref/commit_hook.html
func ( *Conn) ( func() ( bool)) {
	var  int32
	if  != nil {
		 = 1
	}
	.call("sqlite3_commit_hook_go", stk_t(.handle), stk_t())
	.commit = 
}

// RollbackHook registers a callback function to be invoked
// whenever a transaction is rolled back.
//
// https://sqlite.org/c3ref/commit_hook.html
func ( *Conn) ( func()) {
	var  int32
	if  != nil {
		 = 1
	}
	.call("sqlite3_rollback_hook_go", stk_t(.handle), stk_t())
	.rollback = 
}

// UpdateHook registers a callback function to be invoked
// whenever a row is updated, inserted or deleted in a rowid table.
//
// https://sqlite.org/c3ref/update_hook.html
func ( *Conn) ( func( AuthorizerActionCode, ,  string,  int64)) {
	var  int32
	if  != nil {
		 = 1
	}
	.call("sqlite3_update_hook_go", stk_t(.handle), stk_t())
	.update = 
}

func commitCallback( context.Context,  api.Module,  ptr_t) ( int32) {
	if ,  := .Value(connKey{}).(*Conn);  && .handle ==  && .commit != nil {
		if !.commit() {
			 = 1
		}
	}
	return 
}

func rollbackCallback( context.Context,  api.Module,  ptr_t) {
	if ,  := .Value(connKey{}).(*Conn);  && .handle ==  && .rollback != nil {
		.rollback()
	}
}

func updateCallback( context.Context,  api.Module,  ptr_t,  AuthorizerActionCode, ,  ptr_t,  int64) {
	if ,  := .Value(connKey{}).(*Conn);  && .handle ==  && .update != nil {
		 := util.ReadString(, , _MAX_NAME)
		 := util.ReadString(, , _MAX_NAME)
		.update(, , , )
	}
}

// CacheFlush flushes caches to disk mid-transaction.
//
// https://sqlite.org/c3ref/db_cacheflush.html
func ( *Conn) () error {
	 := res_t(.call("sqlite3_db_cacheflush", stk_t(.handle)))
	return .error()
}