Data Access

While it's certainly a challenging and rewarding endeavor to write a database driver from scratch, it's not practical for the complexity.

We will use pgx to talk to a PostgresQL database.

Install pgx

If you haven't initialized Go modules, do so with:

$ go mod init wdgfs

Next, install pgx v5:

$ go get github.com/jackc/pgx/v5/pgxpool

Take a look at the dependencies:

$ cat go.mod
module wdgfs

go 1.23.1

require (
        github.com/jackc/pgpassfile v1.0.0 // indirect
        github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
        github.com/jackc/pgx/v5 v5.7.1 // indirect
        github.com/jackc/puddle/v2 v2.2.2 // indirect
        golang.org/x/crypto v0.27.0 // indirect
        golang.org/x/sync v0.8.0 // indirect
        golang.org/x/text v0.18.0 // indirect
)

Setup PostgresQL

$ docker run --name postgres -p 5432:5432 -e POSTGRES_PASSWORD=my_password -d postgres

Create Database

$ docker exec -it postgres createdb -U postgres mydb

Create Table

Add schema.sql

DROP TABLE IF EXISTS album;
CREATE TABLE album (
  id         SERIAL PRIMARY KEY,
  title      VARCHAR(128) NOT NULL,
  artist     VARCHAR(255) NOT NULL,
  price      NUMERIC(5,2) NOT NULL
);

INSERT INTO album
  (title, artist, price)
VALUES
  ('Blue Train', 'John Coltrane', 56.99),
  ('Giant Steps', 'John Coltrane', 63.99),
  ('Jeru', 'Gerry Mulligan', 17.99),
  ('Sarah Vaughan', 'Sarah Vaughan', 34.98);

Execute the sql file:

$ docker exec -i postgres psql -U postgres mydb < schema.sql

Query PostgresQL

Update server.go with following content:

package main

import (
	"context"
	"fmt"
	"os"

	"github.com/jackc/pgx/v5"
	"github.com/jackc/pgx/v5/pgxpool"
)

type Album struct {
	ID     int64
	Title  string
	Artist string
	Price  float32
}

func main() {
	dbpool, err := pgxpool.New(context.Background(), os.Getenv("DATABASE_URL"))
	if err != nil {
		fmt.Fprintf(os.Stderr, "Unable to create connection pool: %v\n", err)
		os.Exit(1)
	}
	defer dbpool.Close()

	rows, err := dbpool.Query(context.Background(), "select * from album limit 2")
	if err != nil {
		fmt.Fprintf(os.Stderr, "Query failed: %v\n", err)
		os.Exit(1)
	}

	albums, err := pgx.CollectRows(rows, pgx.RowToStructByName[Album])
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to collect rows: %v\n", err)
		os.Exit(1)
	}
	fmt.Println(albums)
}
$ export DATABASE_URL=postgresql://postgres:my_password@localhost:5432/mydb
$ go run server.go
[{1 Blue Train John Coltrane 56.99} {2 Giant Steps John Coltrane 63.99}]