├── .gitignore ├── README.md ├── go.mod ├── go.sum ├── internal └── repository │ ├── db.go │ ├── inventory.sql.go │ ├── item.sql.go │ ├── models.go │ └── player.sql.go ├── main.go ├── migrations ├── 0001_initial.down.sql ├── 0001_initial.up.sql ├── 0002_items.down.sql ├── 0002_items.up.sql ├── 0003_inventory.down.sql ├── 0003_inventory.up.sql ├── 0004_add_gold.down.sql └── 0004_add_gold.up.sql ├── player.go ├── printing.go ├── query.sql ├── query ├── inventory.sql ├── item.sql └── player.sql └── sqlc.yaml /.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SQLc 2 | 3 | This repository contains the code and examples used on the Dreams of Code video about SQLc 4 | 5 | https://youtu.be/VX6KzpjaPp8 6 | 7 | ## Amendments 8 | 9 | Below contains any amendments to the video 10 | 11 | ### Importing 12 | 13 | One thing I glossed over in the video is that you'll need to import the generated repository package following 14 | the codegen. This should be familiar to experienced Go devs but might not to beginners. 15 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/dreamsofcode-io/sqlc 2 | 3 | go 1.22.7 4 | 5 | require ( 6 | github.com/jackc/pgx/v5 v5.7.1 7 | github.com/joho/godotenv v1.5.1 8 | ) 9 | 10 | require ( 11 | github.com/google/uuid v1.6.0 // indirect 12 | github.com/jackc/pgpassfile v1.0.0 // indirect 13 | github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect 14 | golang.org/x/crypto v0.27.0 // indirect 15 | golang.org/x/text v0.18.0 // indirect 16 | ) 17 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 2 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 3 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= 5 | github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 6 | github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= 7 | github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= 8 | github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= 9 | github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= 10 | github.com/jackc/pgx/v5 v5.7.1 h1:x7SYsPBYDkHDksogeSmZZ5xzThcTgRz++I5E+ePFUcs= 11 | github.com/jackc/pgx/v5 v5.7.1/go.mod h1:e7O26IywZZ+naJtWWos6i6fvWK+29etgITqrqHLfoZA= 12 | github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= 13 | github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= 14 | github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= 15 | github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= 16 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 17 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 18 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 19 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 20 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 21 | github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= 22 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 23 | golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= 24 | golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= 25 | golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= 26 | golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 27 | golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= 28 | golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= 29 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 30 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 31 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 32 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 33 | -------------------------------------------------------------------------------- /internal/repository/db.go: -------------------------------------------------------------------------------- 1 | // Code generated by sqlc. DO NOT EDIT. 2 | // versions: 3 | // sqlc v1.27.0 4 | 5 | package repository 6 | 7 | import ( 8 | "context" 9 | 10 | "github.com/jackc/pgx/v5" 11 | "github.com/jackc/pgx/v5/pgconn" 12 | ) 13 | 14 | type DBTX interface { 15 | Exec(context.Context, string, ...interface{}) (pgconn.CommandTag, error) 16 | Query(context.Context, string, ...interface{}) (pgx.Rows, error) 17 | QueryRow(context.Context, string, ...interface{}) pgx.Row 18 | } 19 | 20 | func New(db DBTX) *Queries { 21 | return &Queries{db: db} 22 | } 23 | 24 | type Queries struct { 25 | db DBTX 26 | } 27 | 28 | func (q *Queries) WithTx(tx pgx.Tx) *Queries { 29 | return &Queries{ 30 | db: tx, 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /internal/repository/inventory.sql.go: -------------------------------------------------------------------------------- 1 | // Code generated by sqlc. DO NOT EDIT. 2 | // versions: 3 | // sqlc v1.27.0 4 | // source: inventory.sql 5 | 6 | package repository 7 | 8 | import ( 9 | "context" 10 | 11 | "github.com/google/uuid" 12 | ) 13 | 14 | const addItem = `-- name: AddItem :exec 15 | INSERT INTO inventory (player_id, item_id) 16 | VALUES ($1, $2) 17 | ` 18 | 19 | type AddItemParams struct { 20 | PlayerID int64 `json:"player_id"` 21 | ItemID uuid.UUID `json:"item_id"` 22 | } 23 | 24 | func (q *Queries) AddItem(ctx context.Context, arg AddItemParams) error { 25 | _, err := q.db.Exec(ctx, addItem, arg.PlayerID, arg.ItemID) 26 | return err 27 | } 28 | 29 | const itemsForPlayer = `-- name: ItemsForPlayer :many 30 | SELECT item.id, item.name, item.value 31 | FROM inventory 32 | JOIN item ON item.id = item_id 33 | WHERE player_id = $1 34 | ` 35 | 36 | func (q *Queries) ItemsForPlayer(ctx context.Context, playerID int64) ([]Item, error) { 37 | rows, err := q.db.Query(ctx, itemsForPlayer, playerID) 38 | if err != nil { 39 | return nil, err 40 | } 41 | defer rows.Close() 42 | var items []Item 43 | for rows.Next() { 44 | var i Item 45 | if err := rows.Scan(&i.ID, &i.Name, &i.Value); err != nil { 46 | return nil, err 47 | } 48 | items = append(items, i) 49 | } 50 | if err := rows.Err(); err != nil { 51 | return nil, err 52 | } 53 | return items, nil 54 | } 55 | 56 | const removeItem = `-- name: RemoveItem :exec 57 | DELETE FROM inventory 58 | WHERE player_id = $1 59 | AND item_id = $2 60 | ` 61 | 62 | type RemoveItemParams struct { 63 | PlayerID int64 `json:"player_id"` 64 | ItemID uuid.UUID `json:"item_id"` 65 | } 66 | 67 | func (q *Queries) RemoveItem(ctx context.Context, arg RemoveItemParams) error { 68 | _, err := q.db.Exec(ctx, removeItem, arg.PlayerID, arg.ItemID) 69 | return err 70 | } 71 | -------------------------------------------------------------------------------- /internal/repository/item.sql.go: -------------------------------------------------------------------------------- 1 | // Code generated by sqlc. DO NOT EDIT. 2 | // versions: 3 | // sqlc v1.27.0 4 | // source: item.sql 5 | 6 | package repository 7 | 8 | import ( 9 | "context" 10 | 11 | "github.com/google/uuid" 12 | ) 13 | 14 | const findByID = `-- name: FindByID :one 15 | SELECT id, name, value FROM item WHERE id = $1 16 | ` 17 | 18 | func (q *Queries) FindByID(ctx context.Context, id uuid.UUID) (Item, error) { 19 | row := q.db.QueryRow(ctx, findByID, id) 20 | var i Item 21 | err := row.Scan(&i.ID, &i.Name, &i.Value) 22 | return i, err 23 | } 24 | 25 | const findByName = `-- name: FindByName :one 26 | SELECT id, name, value FROM item WHERE name = $1 27 | ` 28 | 29 | func (q *Queries) FindByName(ctx context.Context, name string) (Item, error) { 30 | row := q.db.QueryRow(ctx, findByName, name) 31 | var i Item 32 | err := row.Scan(&i.ID, &i.Name, &i.Value) 33 | return i, err 34 | } 35 | 36 | const updateName = `-- name: UpdateName :exec 37 | UPDATE item SET name = $1 WHERE id = $2 38 | ` 39 | 40 | type UpdateNameParams struct { 41 | Name string `json:"name"` 42 | ID uuid.UUID `json:"id"` 43 | } 44 | 45 | func (q *Queries) UpdateName(ctx context.Context, arg UpdateNameParams) error { 46 | _, err := q.db.Exec(ctx, updateName, arg.Name, arg.ID) 47 | return err 48 | } 49 | -------------------------------------------------------------------------------- /internal/repository/models.go: -------------------------------------------------------------------------------- 1 | // Code generated by sqlc. DO NOT EDIT. 2 | // versions: 3 | // sqlc v1.27.0 4 | 5 | package repository 6 | 7 | import ( 8 | "time" 9 | 10 | "github.com/google/uuid" 11 | "github.com/jackc/pgx/v5/pgtype" 12 | ) 13 | 14 | type Inventory struct { 15 | PlayerID int64 `json:"player_id"` 16 | ItemID uuid.UUID `json:"item_id"` 17 | } 18 | 19 | type Item struct { 20 | ID uuid.UUID `json:"id"` 21 | Name string `json:"name"` 22 | Value int32 `json:"value"` 23 | } 24 | 25 | type Player struct { 26 | ID int32 `json:"id"` 27 | Name string `json:"name"` 28 | Level int32 `json:"level"` 29 | Class string `json:"class"` 30 | CreatedAt time.Time `json:"created_at"` 31 | UpdatedAt time.Time `json:"updated_at"` 32 | Gold pgtype.Int8 `json:"gold"` 33 | } 34 | -------------------------------------------------------------------------------- /internal/repository/player.sql.go: -------------------------------------------------------------------------------- 1 | // Code generated by sqlc. DO NOT EDIT. 2 | // versions: 3 | // sqlc v1.27.0 4 | // source: player.sql 5 | 6 | package repository 7 | 8 | import ( 9 | "context" 10 | 11 | "github.com/jackc/pgx/v5/pgtype" 12 | ) 13 | 14 | const create = `-- name: Create :one 15 | INSERT INTO player (name, class, level, gold, created_at, updated_at) 16 | VALUES ($1, $2, 1, 0, now(), now()) 17 | RETURNING id, name, level, class, created_at, updated_at, gold 18 | ` 19 | 20 | type CreateParams struct { 21 | Name string `json:"name"` 22 | Class string `json:"class"` 23 | } 24 | 25 | func (q *Queries) Create(ctx context.Context, arg CreateParams) (Player, error) { 26 | row := q.db.QueryRow(ctx, create, arg.Name, arg.Class) 27 | var i Player 28 | err := row.Scan( 29 | &i.ID, 30 | &i.Name, 31 | &i.Level, 32 | &i.Class, 33 | &i.CreatedAt, 34 | &i.UpdatedAt, 35 | &i.Gold, 36 | ) 37 | return i, err 38 | } 39 | 40 | const findPlayerByID = `-- name: FindPlayerByID :one 41 | SELECT id, name, level, class, created_at, updated_at, gold FROM player WHERE id = $1 42 | ` 43 | 44 | func (q *Queries) FindPlayerByID(ctx context.Context, id int32) (Player, error) { 45 | row := q.db.QueryRow(ctx, findPlayerByID, id) 46 | var i Player 47 | err := row.Scan( 48 | &i.ID, 49 | &i.Name, 50 | &i.Level, 51 | &i.Class, 52 | &i.CreatedAt, 53 | &i.UpdatedAt, 54 | &i.Gold, 55 | ) 56 | return i, err 57 | } 58 | 59 | const findPlayerByLevel = `-- name: FindPlayerByLevel :many 60 | SELECT id, name, level, class, created_at, updated_at, gold FROM player WHERE level = $1 61 | ` 62 | 63 | func (q *Queries) FindPlayerByLevel(ctx context.Context, level int32) ([]Player, error) { 64 | rows, err := q.db.Query(ctx, findPlayerByLevel, level) 65 | if err != nil { 66 | return nil, err 67 | } 68 | defer rows.Close() 69 | var items []Player 70 | for rows.Next() { 71 | var i Player 72 | if err := rows.Scan( 73 | &i.ID, 74 | &i.Name, 75 | &i.Level, 76 | &i.Class, 77 | &i.CreatedAt, 78 | &i.UpdatedAt, 79 | &i.Gold, 80 | ); err != nil { 81 | return nil, err 82 | } 83 | items = append(items, i) 84 | } 85 | if err := rows.Err(); err != nil { 86 | return nil, err 87 | } 88 | return items, nil 89 | } 90 | 91 | const findPlayerByName = `-- name: FindPlayerByName :one 92 | SELECT id, name, level, class, created_at, updated_at, gold FROM player WHERE name = $1 93 | ` 94 | 95 | func (q *Queries) FindPlayerByName(ctx context.Context, name string) (Player, error) { 96 | row := q.db.QueryRow(ctx, findPlayerByName, name) 97 | var i Player 98 | err := row.Scan( 99 | &i.ID, 100 | &i.Name, 101 | &i.Level, 102 | &i.Class, 103 | &i.CreatedAt, 104 | &i.UpdatedAt, 105 | &i.Gold, 106 | ) 107 | return i, err 108 | } 109 | 110 | const incrGold = `-- name: IncrGold :one 111 | UPDATE player 112 | SET gold = gold + $1::int 113 | WHERE id = $2::bigint 114 | RETURNING gold 115 | ` 116 | 117 | type IncrGoldParams struct { 118 | Amount int32 `json:"amount"` 119 | PlayerID int64 `json:"player_id"` 120 | } 121 | 122 | func (q *Queries) IncrGold(ctx context.Context, arg IncrGoldParams) (pgtype.Int8, error) { 123 | row := q.db.QueryRow(ctx, incrGold, arg.Amount, arg.PlayerID) 124 | var gold pgtype.Int8 125 | err := row.Scan(&gold) 126 | return gold, err 127 | } 128 | 129 | const updateLevel = `-- name: UpdateLevel :exec 130 | UPDATE player SET level = $1 WHERE id = $2 131 | ` 132 | 133 | type UpdateLevelParams struct { 134 | Level int32 `json:"level"` 135 | ID int32 `json:"id"` 136 | } 137 | 138 | func (q *Queries) UpdateLevel(ctx context.Context, arg UpdateLevelParams) error { 139 | _, err := q.db.Exec(ctx, updateLevel, arg.Level, arg.ID) 140 | return err 141 | } 142 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "os" 6 | 7 | "github.com/google/uuid" 8 | "github.com/jackc/pgx/v5" 9 | _ "github.com/joho/godotenv/autoload" 10 | ) 11 | 12 | func main() { 13 | ctx := context.Background() 14 | 15 | conn, err := pgx.Connect(context.Background(), os.Getenv("DATABASE_URL")) 16 | if err != nil { 17 | os.Exit(1) 18 | } 19 | defer conn.Close(ctx) 20 | 21 | playerID := int64(1) 22 | itemID := uuid.MustParse("74feda80-8c1a-428a-af1e-52a2a721284f") 23 | 24 | SellItem(ctx, conn, playerID, itemID) 25 | } 26 | -------------------------------------------------------------------------------- /migrations/0001_initial.down.sql: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dreamsofcode-io/sqlc/913e39855a33b4ad20b22e20c9512bfd63de1d1f/migrations/0001_initial.down.sql -------------------------------------------------------------------------------- /migrations/0001_initial.up.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE player( 2 | id SERIAL PRIMARY KEY, 3 | name VARCHAR NOT NULL, 4 | level INT NOT NULL, 5 | class VARCHAR NOT NULL, 6 | created_at TIMESTAMPTZ NOT NULL, 7 | updated_at TIMESTAMPTZ NOT NULL 8 | ); 9 | 10 | CREATE UNIQUE INDEX on player(name); 11 | 12 | INSERT INTO player (id, name, level, class, created_at, updated_at) VALUES 13 | (1, 'Sephiro', 32, 'Warrior', '2024-01-15 14:23:45Z', '2024-02-10 08:30:15Z'), 14 | (2, 'SpellQueen', 47, 'Mage', '2023-12-05 10:15:12Z', '2024-01-22 16:45:22Z'), 15 | (3, 'StormcallerX', 29, 'Druid', '2023-11-20 09:20:34Z', '2024-01-10 12:10:58Z'), 16 | (4, 'ShadowMaster', 50, 'Rogue', '2023-10-08 19:50:14Z', '2023-12-18 11:35:45Z'), 17 | (5, 'FireFurry', 55, 'Sorcerer', '2023-09-03 17:45:23Z', '2023-11-14 14:20:30Z'); 18 | -------------------------------------------------------------------------------- /migrations/0002_items.down.sql: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dreamsofcode-io/sqlc/913e39855a33b4ad20b22e20c9512bfd63de1d1f/migrations/0002_items.down.sql -------------------------------------------------------------------------------- /migrations/0002_items.up.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE item ( 2 | id UUID PRIMARY KEY, 3 | name VARCHAR NOT NULL, 4 | value INT NOT NULL 5 | ); 6 | 7 | CREATE UNIQUE INDEX ON item(name); 8 | -------------------------------------------------------------------------------- /migrations/0003_inventory.down.sql: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dreamsofcode-io/sqlc/913e39855a33b4ad20b22e20c9512bfd63de1d1f/migrations/0003_inventory.down.sql -------------------------------------------------------------------------------- /migrations/0003_inventory.up.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE inventory ( 2 | player_id BIGINT NOT NULL REFERENCES player(id), 3 | item_id UUID NOT NULL REFERENCES item(id), 4 | PRIMARY KEY(player_id, item_id) 5 | ); 6 | 7 | CREATE INDEX ON inventory(player_id); 8 | 9 | INSERT INTO item (id, name, value) VALUES 10 | ('42c6294c-56de-49d2-be2e-055b2a2151a6', 'Rusty Sword', 100), 11 | ('2e9e9593-c5ec-4554-9e15-131aa0b63127', 'Bat Wing',6), 12 | ('0faffedc-2047-4616-9357-51d22fe80ff7', 'Skeleton Femur', 2); 13 | 14 | INSERT INTO inventory (player_id, item_id) VALUES 15 | (1, '42c6294c-56de-49d2-be2e-055b2a2151a6'), 16 | (2, '2e9e9593-c5ec-4554-9e15-131aa0b63127'), 17 | (3, '0faffedc-2047-4616-9357-51d22fe80ff7'), 18 | (4, '42c6294c-56de-49d2-be2e-055b2a2151a6'), 19 | (5, '0faffedc-2047-4616-9357-51d22fe80ff7'); 20 | -------------------------------------------------------------------------------- /migrations/0004_add_gold.down.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE player DROP COLUMN gold; 2 | -------------------------------------------------------------------------------- /migrations/0004_add_gold.up.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE player ADD COLUMN gold bigint default 0; 2 | ALTER TABLE player ADD CONSTRAINT chk_gold_non_negative CHECK (gold >= 0); 3 | -------------------------------------------------------------------------------- /player.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | "github.com/google/uuid" 8 | "github.com/jackc/pgx/v5" 9 | 10 | "github.com/dreamsofcode-io/sqlc/internal/repository" 11 | ) 12 | 13 | func SellItem( 14 | ctx context.Context, 15 | conn *pgx.Conn, 16 | playerID int64, 17 | itemID uuid.UUID, 18 | ) error { 19 | tx, _ := conn.Begin(ctx) 20 | defer tx.Rollback(ctx) 21 | 22 | repo := repository.New(tx) 23 | 24 | item, err := repo.GetInventoryItem(ctx, repository.GetInventoryItemParams{ 25 | PlayerID: playerID, 26 | ItemID: itemID, 27 | }) 28 | if err != nil { 29 | return err 30 | } 31 | 32 | count, _ := repo.RemoveInventoryItem(ctx, repository.RemoveInventoryItemParams{ 33 | PlayerID: playerID, 34 | ItemID: itemID, 35 | }) 36 | if count == 0 { 37 | return fmt.Errorf("no item to remove") 38 | } 39 | 40 | player, err := repo.IncrPlayerGold(ctx, repository.IncrPlayerGoldParams{ 41 | PlayerID: playerID, 42 | Amount: item.Value, 43 | }) 44 | if err != nil { 45 | fmt.Println("IncrPlayerGold query failed!") 46 | return err 47 | } 48 | 49 | if err = tx.Commit(ctx); err != nil { 50 | return err 51 | } 52 | 53 | fmt.Println("Item sold! Player Gold:", player.Gold.Int64) 54 | 55 | return nil 56 | } 57 | -------------------------------------------------------------------------------- /printing.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "text/tabwriter" 7 | ) 8 | 9 | func newWriter() *tabwriter.Writer { 10 | w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) 11 | fmt.Fprintln(w, "ID\tName\tClass\tLevel\tCreated At\tUpdated At") 12 | return w 13 | } 14 | 15 | // 16 | // func printPlayer(x player.Player) { 17 | // w := newWriter() 18 | // defer w.Flush() 19 | // 20 | // printPlayerLine(w, x) 21 | // } 22 | // 23 | // func printPlayerLine(w io.Writer, player player.Player) { 24 | // fmt.Fprintf(w, 25 | // "%d\t%s\t%s\t%d\t%s\t%s\n", 26 | // player.ID, 27 | // player.Name, 28 | // player.Class, 29 | // player.Level, 30 | // player.CreatedAt.UTC().Format(time.RFC3339), 31 | // player.UpdatedAt.UTC().Format(time.RFC3339), 32 | // ) 33 | // } 34 | // 35 | // func printPlayers(xs []player.Player) { 36 | // w := newWriter() 37 | // defer w.Flush() 38 | // 39 | // for _, x := range xs { 40 | // printPlayerLine(w, x) 41 | // } 42 | // } 43 | -------------------------------------------------------------------------------- /query.sql: -------------------------------------------------------------------------------- 1 | -- name: FindAllPlayers :many 2 | SELECT * FROM player 3 | ORDER BY level DESC; 4 | 5 | -- name: InsertItem :one 6 | INSERT INTO item (id, name, value) 7 | VALUES (uuid_generate_v4(), $1, $2) 8 | RETURNING *; 9 | 10 | -- name: FindItemByID :one 11 | SELECT * FROM item 12 | WHERE id = $1; 13 | 14 | -- name: GetInventoryAndPlayer :many 15 | SELECT player.*, item.* 16 | FROM inventory 17 | JOIN player ON player.id = player_id 18 | JOIN item ON item.id = item_id; 19 | 20 | -- name: GetInventoryItem :one 21 | SELECT item.* 22 | FROM inventory 23 | JOIN item ON item.id = item_id 24 | WHERE player_id = $1 25 | AND item_id = $2; 26 | 27 | -- name: RemoveInventoryItem :execrows 28 | DELETE FROM inventory 29 | WHERE player_id = $1 30 | AND item_id = $2; 31 | 32 | -- name: IncrPlayerGold :one 33 | UPDATE player 34 | SET gold = gold + sqlc.arg(amount)::int 35 | WHERE id = sqlc.arg(player_id)::bigint 36 | RETURNING *; 37 | -------------------------------------------------------------------------------- /query/inventory.sql: -------------------------------------------------------------------------------- 1 | -- name: AddItem :exec 2 | INSERT INTO inventory (player_id, item_id) 3 | VALUES ($1, $2); 4 | 5 | -- name: RemoveItem :exec 6 | DELETE FROM inventory 7 | WHERE player_id = $1 8 | AND item_id = $2; 9 | 10 | -- name: ItemsForPlayer :many 11 | SELECT item.* 12 | FROM inventory 13 | JOIN item ON item.id = item_id 14 | WHERE player_id = $1; 15 | -------------------------------------------------------------------------------- /query/item.sql: -------------------------------------------------------------------------------- 1 | -- name: FindByID :one 2 | SELECT * FROM item WHERE id = $1; 3 | 4 | -- name: FindByName :one 5 | SELECT * FROM item WHERE name = $1; 6 | 7 | -- name: UpdateName :exec 8 | UPDATE item SET name = $1 WHERE id = $2; 9 | -------------------------------------------------------------------------------- /query/player.sql: -------------------------------------------------------------------------------- 1 | -- name: FindPlayerByID :one 2 | SELECT * FROM player WHERE id = $1; 3 | 4 | -- name: FindPlayerByName :one 5 | SELECT * FROM player WHERE name = $1; 6 | 7 | -- name: FindPlayerByLevel :many 8 | SELECT * FROM player WHERE level = $1; 9 | 10 | -- name: Create :one 11 | INSERT INTO player (name, class, level, gold, created_at, updated_at) 12 | VALUES ($1, $2, 1, 0, now(), now()) 13 | RETURNING *; 14 | 15 | -- name: UpdateLevel :exec 16 | UPDATE player SET level = $1 WHERE id = $2; 17 | 18 | -- name: IncrGold :one 19 | UPDATE player 20 | SET gold = gold + sqlc.arg(amount)::int 21 | WHERE id = sqlc.arg(player_id)::bigint 22 | RETURNING gold; 23 | -------------------------------------------------------------------------------- /sqlc.yaml: -------------------------------------------------------------------------------- 1 | version: "2" 2 | sql: 3 | - engine: "postgresql" 4 | schema: "./migrations" 5 | queries: "./query" 6 | gen: 7 | go: 8 | emit_json_tags: true 9 | package: "repository" 10 | out: "internal/repository" 11 | sql_package: "pgx/v5" 12 | overrides: 13 | - db_type: "uuid" 14 | go_type: 15 | import: "github.com/google/uuid" 16 | type: "UUID" 17 | - db_type: "timestamptz" 18 | go_type: 19 | import: "time" 20 | type: "Time" 21 | --------------------------------------------------------------------------------