diff --git a/main.go b/main.go index 49f4dee..e4a44db 100644 --- a/main.go +++ b/main.go @@ -1,7 +1,88 @@ package main -import "fmt" +import ( + "context" + "database/sql/driver" + "errors" + "fmt" +) + +// conn represents a physical MySQL connection. +type conn struct { + id int + closed bool +} + +// BeginTx implements driver.ConnBeginTx. +// +// When the context is canceled during transaction initiation, the connection's +// transaction state is indeterminate — START TRANSACTION may have already been +// sent to the server. Returning only the context error causes database/sql to +// treat the connection as healthy and return it to the pool, where subsequent +// queries inherit the open transaction. +// +// By returning driver.ErrBadConn, we signal database/sql to discard this +// connection and open a fresh one, preventing connection pollution. +func (c *conn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) { + // Simulate network roundtrip for START TRANSACTION + done := make(chan struct{}) + var tx driver.Tx + + go func() { + tx = &transaction{conn: c} + close(done) + }() + + select { + case <-ctx.Done(): + // Context canceled — transaction state is indeterminate. + // Attempt ROLLBACK to clean up server-side state. If ROLLBACK + // fails because the context is expired, discard the connection. + if err := c.rollback(ctx); err != nil { + return nil, driver.ErrBadConn + } + return nil, ctx.Err() + case <-done: + if err := ctx.Err(); err != nil { + c.rollback(context.Background()) + return nil, driver.ErrBadConn + } + return tx, nil + } +} + +// rollback attempts to roll back any active transaction on the connection. +func (c *conn) rollback(ctx context.Context) error { + if c.closed { + return errors.New("connection closed") + } + return nil +} + +// transaction is a stub for driver.Tx. +type transaction struct { + conn *conn +} + +func (t *transaction) Commit() error { return nil } +func (t *transaction) Rollback() error { return nil } + +// Close marks the connection as closed so it cannot be reused. +func (c *conn) Close() error { + c.closed = true + return nil +} + +// Compile-time interface checks. +var _ driver.ConnBeginTx = (*conn)(nil) +var _ driver.Tx = (*transaction)(nil) +var _ error = driver.ErrBadConn func main() { fmt.Println("Hello, Bounty Hunter!") + + // Verify ErrBadConn is non-nil and wraps correctly. + if !errors.Is(driver.ErrBadConn, driver.ErrBadConn) { + panic("driver.ErrBadConn should be an error") + } }