๐Ÿ—„๏ธdatabase/sqlLESSON

database/sql

Go's database/sql package provides a driver-agnostic API for relational databases. The same code works for PostgreSQL, MySQL, SQLite, and any other database that has a Go driver โ€” you just swap the driver import and the DSN string.

Opening a connection

sql.Open does not connect โ€” it only validates the arguments and creates the handle. Call db.Ping() to verify the database is reachable:

Querying a single row

QueryRow always returns a *Row. The error (if any) is deferred until Scan.

Querying multiple rows

Always defer rows.Close() โ€” an unclosed Rows holds a connection from the pool until the function returns.

Executing mutations

Prepared statements

Prepared statements are parsed once on the server, then executed many times with different parameters:

Beyond performance, prepared statements prevent SQL injection: user input is always treated as a parameter value, never as part of the query string.

Transactions

The defer tx.Rollback() pattern is idiomatic: if Commit succeeds, the subsequent Rollback is a no-op. If anything goes wrong before Commit, the deferred Rollback cleans up automatically.

Connection pool settings

sql.DB maintains a pool of connections. Tune it for your workload:

Too few open connections โ†’ throughput bottleneck. Too many โ†’ database server overloaded. Typical starting point: MaxOpenConns = 4 ร— CPU cores.

Knowledge Check

Why must you always `defer rows.Close()` after db.Query?

How do prepared statements prevent SQL injection?

What is the purpose of `defer tx.Rollback()` when combined with an explicit `tx.Commit()` call?