Lab 12: Database SQL

Time: 30 minutes | Level: Practitioner | Docker: docker run -it --rm golang:1.22-alpine sh

Overview

Go's database/sql package provides a generic SQL interface. Drivers plug in via _ imports. We use modernc.org/sqlite — a pure-Go SQLite driver requiring no CGo.

Setup

mkdir -p /tmp/sqllab && cd /tmp/sqllab
cat > go.mod << 'EOF'
module example.com/sqllab
go 1.22
EOF
go get modernc.org/[email protected]

Step 1: Open and Ping

package main

import (
    "database/sql"
    "fmt"
    "log"
    _ "modernc.org/sqlite"
)

func main() {
    // :memory: = in-memory database
    db, err := sql.Open("sqlite", ":memory:")
    if err != nil {
        log.Fatal(err)
    }
    defer db.Close()

    // Verify connectivity
    if err := db.Ping(); err != nil {
        log.Fatal("ping failed:", err)
    }
    fmt.Println("connected to SQLite in-memory DB")
}

Step 2: Create Table and Insert

Step 3: QueryRow — Single Row

💡 Tip: Always check for sql.ErrNoRows when using QueryRow. Other errors indicate query problems.

Step 4: Query — Multiple Rows

Step 5: Transactions

Step 6: Connection Pool Configuration

Step 7: Error Handling and NULL Values

Step 8: Capstone — Full CRUD

📸 Verified Output:

Summary

API
Purpose
Notes

sql.Open(driver, dsn)

Create db handle

Lazy — doesn't connect yet

db.Ping()

Verify connection

Returns error if unreachable

db.Exec(query, args...)

INSERT/UPDATE/DELETE

Returns (Result, error)

db.QueryRow(q, args...).Scan()

Single row

Check sql.ErrNoRows

db.Query(q, args...)

Multiple rows

defer rows.Close() + rows.Err()

db.Prepare(q)

Compile SQL once

defer stmt.Close()

db.Begin()

Start transaction

Returns *sql.Tx

tx.Commit() / tx.Rollback()

End transaction

Rollback on error

sql.NullString

Nullable string column

.Valid + .String

SetMaxOpenConns(n)

Pool size

Set for production

Last updated