package bnwasm import ( "context" "errors" "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgconn" ) // errNestedTx is returned by Tx.Begin. pgx models a nested Begin as a SAVEPOINT; // the db.* ABI has no savepoint verb (v1 scope), and a grep of the plugin fleet // (symposium, messenger) found no nested-transaction/savepoint use, so this is // rejected explicitly rather than silently degrading correctness. var errNestedTx = errors.New("bnwasm: nested transactions (savepoints) are not supported") // Tx is a pgx.Tx bound to a host-side transaction handle. Every Exec/Query/ // QueryRow carries the handle so the host runs it on the transaction's // connection; Commit/Rollback release it. After either, the Tx is closed and // further statements return pgx.ErrTxClosed. The host also drops the handle at // the call chain's deadline (WO-WZ-007), so a guest that leaks a Tx without // committing has it rolled back host-side — a guest can never pin a connection. type Tx struct { t Transport handle uint64 closed bool } // Begin would start a savepoint-backed nested transaction: unsupported. func (tx *Tx) Begin(ctx context.Context) (pgx.Tx, error) { return nil, errNestedTx } func (tx *Tx) Commit(ctx context.Context) error { if tx.closed { return pgx.ErrTxClosed } tx.closed = true return tx.t.txCommit(ctx, tx.handle) } func (tx *Tx) Rollback(ctx context.Context) error { if tx.closed { return pgx.ErrTxClosed } tx.closed = true return tx.t.txRollback(ctx, tx.handle) } func (tx *Tx) Exec(ctx context.Context, sql string, args ...any) (pgconn.CommandTag, error) { if tx.closed { return pgconn.CommandTag{}, pgx.ErrTxClosed } return tx.t.exec(ctx, sql, tx.handle, args) } func (tx *Tx) Query(ctx context.Context, sql string, args ...any) (pgx.Rows, error) { if tx.closed { return errRows(pgx.ErrTxClosed), pgx.ErrTxClosed } resp, err := tx.t.query(ctx, sql, tx.handle, args) if err != nil { return errRows(err), err } return newPgxRows(resp), nil } func (tx *Tx) QueryRow(ctx context.Context, sql string, args ...any) pgx.Row { if tx.closed { return &pgxRow{err: pgx.ErrTxClosed} } resp, err := tx.t.query(ctx, sql, tx.handle, args) if err != nil { return &pgxRow{err: err} } return &pgxRow{rows: newPgxRows(resp)} } // --- Unsupported pgx.Tx surface (not emitted by sqlc; explicit errors) --- func (tx *Tx) CopyFrom(context.Context, pgx.Identifier, []string, pgx.CopyFromSource) (int64, error) { return 0, errors.New("bnwasm: CopyFrom is not supported over the wasm DB ABI") } func (tx *Tx) SendBatch(context.Context, *pgx.Batch) pgx.BatchResults { return errBatchResults{errors.New("bnwasm: SendBatch is not supported over the wasm DB ABI")} } func (tx *Tx) LargeObjects() pgx.LargeObjects { return pgx.LargeObjects{} } func (tx *Tx) Prepare(context.Context, string, string) (*pgconn.StatementDescription, error) { return nil, errors.New("bnwasm: Prepare is not supported over the wasm DB ABI") } func (tx *Tx) Conn() *pgx.Conn { return nil } // errBatchResults is a pgx.BatchResults that reports the same error from every // method, so an unsupported SendBatch surfaces cleanly instead of nil-panicking. type errBatchResults struct{ err error } func (e errBatchResults) Exec() (pgconn.CommandTag, error) { return pgconn.CommandTag{}, e.err } func (e errBatchResults) Query() (pgx.Rows, error) { return errRows(e.err), e.err } func (e errBatchResults) QueryRow() pgx.Row { return &pgxRow{err: e.err} } func (e errBatchResults) Close() error { return e.err }