package bnwasm import ( "errors" "fmt" abiv1 "git.dev.alexdunmow.com/block/pluginsdk/abi/v1" "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgconn" ) // pgxRows adapts a buffered DbRowsResponse to the pgx.Rows interface. The full // result set is already materialized (the host returns it in one message), so // iteration is a slice cursor. Only the methods sqlc-generated code exercises // carry real behavior; the rest satisfy the interface. type pgxRows struct { columns []string rows []*abiv1.DbRow idx int // index of the row Next() last advanced to; -1 before first Next err error closed bool } func newPgxRows(resp *abiv1.DbRowsResponse) *pgxRows { return &pgxRows{columns: resp.GetColumns(), rows: resp.GetRows(), idx: -1} } func (r *pgxRows) Close() { r.closed = true } func (r *pgxRows) Err() error { return r.err } func (r *pgxRows) CommandTag() pgconn.CommandTag { return pgconn.NewCommandTag(fmt.Sprintf("SELECT %d", len(r.rows))) } func (r *pgxRows) FieldDescriptions() []pgconn.FieldDescription { fds := make([]pgconn.FieldDescription, len(r.columns)) for i, c := range r.columns { fds[i] = pgconn.FieldDescription{Name: c} } return fds } func (r *pgxRows) Next() bool { if r.err != nil || r.closed { return false } if r.idx+1 >= len(r.rows) { r.Close() // pgx auto-closes when iteration is exhausted return false } r.idx++ return true } func (r *pgxRows) Scan(dest ...any) error { if r.err != nil { return r.err } if r.idx < 0 || r.idx >= len(r.rows) { return errors.New("bnwasm: Scan called without a current row (call Next first)") } if err := scanRow(r.rows[r.idx], dest); err != nil { r.err = err r.Close() return err } return nil } func (r *pgxRows) Values() ([]any, error) { if r.idx < 0 || r.idx >= len(r.rows) { return nil, errors.New("bnwasm: Values called without a current row") } cells := r.rows[r.idx].GetValues() out := make([]any, len(cells)) for i, c := range cells { v, _ := naturalValue(c) out[i] = v } return out, nil } func (r *pgxRows) RawValues() [][]byte { return nil } func (r *pgxRows) Conn() *pgx.Conn { return nil } // pgxRow is the single-row QueryRow result. Like pgx, it defers all errors // (including the query error) to Scan, and reports pgx.ErrNoRows when empty. type pgxRow struct { rows *pgxRows err error } func (r *pgxRow) Scan(dest ...any) error { if r.err != nil { return r.err } defer r.rows.Close() if !r.rows.Next() { if r.rows.Err() != nil { return r.rows.Err() } return pgx.ErrNoRows } return r.rows.Scan(dest...) } func scanRow(row *abiv1.DbRow, dest []any) error { cells := row.GetValues() if len(dest) != len(cells) { return fmt.Errorf("bnwasm: scan expected %d destinations, got %d columns", len(dest), len(cells)) } for i, d := range dest { if err := scanValue(cells[i], d); err != nil { return fmt.Errorf("bnwasm: scan column %d: %w", i, err) } } return nil }