Installations
-
go install github.com/pressly/goose/v3/cmd/goose@latest
-
- For mac and linux users, run
and from now ongo install github.com/sqlc-dev/sqlc/cmd/sqlc@latest
sqlc generate
will be available in your shell. - For windows user, the
sqlc
package above wouldn't work, instead we run
indocker run --rm -v "%cd%:/src" -w /src sqlc/sqlc generate
cmd
.
- For mac and linux users, run
Workflow for Database Migrations in Postgresql
In case the reader use mySQL
, the only difference will be the sqlc.yml
.
sql/scehma
sql/schema/001_users.sql
-- +goose Up CREATE TABLE users ( id UUID PRIMARY KEY, created_at TIMESTAMP NOT NULL, updated_at TIMESTAMP NOT NULL, name TEXT NOT NULL ); -- +goose Down DROP TABLE users;
sql/schema/002_users_apikey.sql
-- +goose Up ALTER TABLE users ADD COLUMN api_key VARCHAR(64) UNIQUE NOT NULL DEFAULT ( encode(sha256(random()::text::bytea), 'hex') ); -- +goose Down ALTER TABLE users DROP COLUMN api_key;
sql/schema/003_feeds.sql
-- +goose Up CREATE TABLE feeds ( id UUID PRIMARY KEY, created_at TIMESTAMP NOT NULL, updated_at TIMESTAMP NOT NULL, name TEXT NOT NULL, url TEXT UNIQUE NOT NULL, user_id UUID REFERENCES users(id) ON DELETE CASCADE ); -- +goose Down DROP TABLE users;
sql/schema/004_feeds_uuid_nonull.sql
-- +goose Up ALTER TABLE feeds ALTER COLUMN user_id SET NOT NULL; -- +goose Down ALTER TABLE feeds ALTER COLUMN user_id DROP NOT NULL;
Apply Changes in DB Migrations by Goose
-
After each migration is inserted, we run
sh db_migrate_up.sh
, where# db_migrate_up.sh DB_URL=postgresql://pguser:pguser@127.0.0.1:5432/rssagg cd sql/schema goose postgres $DB_URL up read -p "Press any key to leave ..."
Create sqlc.yaml (Only do it Once)
-
Create a
sqlc.yaml
at the root project level:version: "2" sql: - schema: "sql/schema" queries: "sql/queries" engine: "postgresql" gen: go: out: "internal/database"
-
For
mySQL
the yaml fiile is more or less the same, which can be copied form documentation.
sql/queries
sql/queries/users.sql
-- name: CreateUser :one INSERT INTO users(id, created_at, updated_at, name, api_key) VALUES ($1, $2, $3, $4, encode(sha256(random()::text::bytea), 'hex') ) RETURNING *; -- name: GetUserByAPIKey :one SELECT * FROM users WHERE api_key = $1;
sql/quries/feeds.sql
-- name: CreateFeed :one INSERT INTO feeds (id, created_at, updated_at, name, url, user_id) VALUES ($1, $2, $3, $4, $5, $6) RETURNING *;
Execute sqlc generate
- After table migration is done, we create correponding schema as struct, queries as functions in
go
:
or in windows cmd prompt (or create asqlc generate
.bat
file):docker run --rm -v "%cd%:/src" -w /src sqlc/sqlc generate pause
Outputs from Sqlc Generate
internal/database/db.go
// Code generated by sqlc. DO NOT EDIT. // versions: // sqlc v1.22.0 package database import ( "context" "database/sql" ) type DBTX interface { ExecContext(context.Context, string, ...interface{}) (sql.Result, error) PrepareContext(context.Context, string) (*sql.Stmt, error) QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error) QueryRowContext(context.Context, string, ...interface{}) *sql.Row } func New(db DBTX) *Queries { return &Queries{db: db} } type Queries struct { db DBTX } func (q *Queries) WithTx(tx *sql.Tx) *Queries { return &Queries{ db: tx, } }
internal/database/models.go
// Code generated by sqlc. DO NOT EDIT. // versions: // sqlc v1.22.0 package database import ( "time" "github.com/google/uuid" ) type Feed struct { ID uuid.UUID CreatedAt time.Time UpdatedAt time.Time Name string Url string UserID uuid.UUID } type User struct { ID uuid.UUID CreatedAt time.Time UpdatedAt time.Time Name string ApiKey string }
internal/database/users.sql.go
// Code generated by sqlc. DO NOT EDIT. // versions: // sqlc v1.22.0 // source: users.sql package database import ( "context" "time" "github.com/google/uuid" ) const createUser = `-- name: CreateUser :one INSERT INTO users(id, created_at, updated_at, name, api_key) VALUES ($1, $2, $3, $4, encode(sha256(random()::text::bytea), 'hex') ) RETURNING id, created_at, updated_at, name, api_key ` type CreateUserParams struct { ID uuid.UUID CreatedAt time.Time UpdatedAt time.Time Name string } func (q *Queries) CreateUser(ctx context.Context, arg CreateUserParams) (User, error) { row := q.db.QueryRowContext(ctx, createUser, arg.ID, arg.CreatedAt, arg.UpdatedAt, arg.Name, ) var i User err := row.Scan( &i.ID, &i.CreatedAt, &i.UpdatedAt, &i.Name, &i.ApiKey, ) return i, err } const getUserByAPIKey = `-- name: GetUserByAPIKey :one SELECT id, created_at, updated_at, name, api_key FROM users WHERE api_key = $1 ` func (q *Queries) GetUserByAPIKey(ctx context.Context, apiKey string) (User, error) { row := q.db.QueryRowContext(ctx, getUserByAPIKey, apiKey) var i User err := row.Scan( &i.ID, &i.CreatedAt, &i.UpdatedAt, &i.Name, &i.ApiKey, ) return i, err }
internal/database/feeds.sql.go
// Code generated by sqlc. DO NOT EDIT. // versions: // sqlc v1.22.0 // source: feeds.sql package database import ( "context" "time" "github.com/google/uuid" ) const createFeed = `-- name: CreateFeed :one INSERT INTO feeds (id, created_at, updated_at, name, url, user_id) VALUES ($1, $2, $3, $4, $5, $6) RETURNING id, created_at, updated_at, name, url, user_id ` type CreateFeedParams struct { ID uuid.UUID CreatedAt time.Time UpdatedAt time.Time Name string Url string UserID uuid.UUID } func (q *Queries) CreateFeed(ctx context.Context, arg CreateFeedParams) (Feed, error) { row := q.db.QueryRowContext(ctx, createFeed, arg.ID, arg.CreatedAt, arg.UpdatedAt, arg.Name, arg.Url, arg.UserID, ) var i Feed err := row.Scan( &i.ID, &i.CreatedAt, &i.UpdatedAt, &i.Name, &i.Url, &i.UserID, ) return i, err }