package registry import ( "context" "database/sql" "database/sql/driver" "errors" ) type claimTransaction struct { conn *sql.Conn ctx context.Context full bool done bool } func (r *Registry) beginClaimTransaction(ctx context.Context, full bool) (*claimTransaction, error) { conn, err := r.sql.Conn(ctx) if err != nil { return nil, err } tx := &claimTransaction{conn: conn, ctx: ctx, full: full} if full { if _, err = conn.ExecContext(ctx, "PRAGMA synchronous=FULL"); err != nil { return nil, errors.Join(err, tx.close()) } } if _, err = conn.ExecContext(ctx, "BEGIN IMMEDIATE"); err != nil { return nil, errors.Join(err, tx.close()) } return tx, nil } func (t *claimTransaction) ExecContext(ctx context.Context, q string, args ...any) (sql.Result, error) { return t.conn.ExecContext(ctx, q, args...) } func (t *claimTransaction) QueryRowContext(ctx context.Context, q string, args ...any) *sql.Row { return t.conn.QueryRowContext(ctx, q, args...) } func (t *claimTransaction) Exec(q string, args ...any) (sql.Result, error) { return t.ExecContext(t.ctx, q, args...) } func (t *claimTransaction) QueryRow(q string, args ...any) *sql.Row { return t.QueryRowContext(t.ctx, q, args...) } func (t *claimTransaction) close() error { t.done = true var err error if t.full { _, err = t.conn.ExecContext(context.Background(), "PRAGMA synchronous=NORMAL") if err != nil { _ = t.conn.Raw(func(any) error { return driver.ErrBadConn }) } } return errors.Join(err, t.conn.Close()) } func (t *claimTransaction) Commit() error { if t.done { return sql.ErrTxDone } if _, err := t.conn.ExecContext(t.ctx, "COMMIT"); err != nil { return errors.Join(err, t.Rollback()) } return t.close() } func (t *claimTransaction) Rollback() error { if t.done { return nil } _, err := t.conn.ExecContext(context.Background(), "ROLLBACK") if err != nil { _ = t.conn.Raw(func(any) error { return driver.ErrBadConn }) } return errors.Join(err, t.close()) }