Compare · 002

Choosing a command: one, many, exec, execrows, execresult: SQLite and CockroachDB

Every file of the SQLite and CockroachDB versions side by side, with the lines that differ marked.

Configuration

sgl.yamldiffers
SQLite
CockroachDB
1version: 1
1version: 1
2
2
3databases:
3databases:
4 - name: harbour
4 - name: harbour
5 dialect: sqlite
5 dialect: postgres
6 migrations: db/migrations
6 migrations: db/migrations
7 queries: db/queries
7 queries: db/queries
8 go:
8 go:
9 package: store
9 package: store
10 out: internal/store
10 out: internal/store
11 driver: database/sql

Migrations

db/migrations/0001_boats.up.sqldiffers
SQLite
CockroachDB
1CREATE TABLE boats (
1CREATE TABLE boats (
2 id INTEGER PRIMARY KEY,
2 id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
3 name TEXT NOT NULL,
3 name TEXT NOT NULL,
4 berth INTEGER NOT NULL,
4 berth BIGINT NOT NULL,
5 at_sea BOOLEAN NOT NULL DEFAULT 0
5 at_sea BOOLEAN NOT NULL DEFAULT false
6);
6);
db/migrations/0001_boats.down.sqlidentical
SQLite
CockroachDB
1DROP TABLE boats;
1DROP TABLE boats;

Queries

MoorBoatdiffers
SQLite · db/queries/boats.sql
CockroachDB · db/queries/boats.sql
1-- sgl.query(MoorBoat, execresult)
1-- sgl.query(MoorBoat, one)
2INSERT INTO boats (name, berth) VALUES (?, ?);
2INSERT INTO boats (name, berth) VALUES ($1, $2) RETURNING id;
GetBoatdiffers
SQLite · db/queries/boats.sql
CockroachDB · db/queries/boats.sql
1-- sgl.query(GetBoat, one)
1-- sgl.query(GetBoat, one)
2SELECT id, name, berth, at_sea FROM boats WHERE id = ?;
2SELECT id, name, berth, at_sea FROM boats WHERE id = $1;
ListBoatsidentical
SQLite · db/queries/boats.sql
CockroachDB · db/queries/boats.sql
1-- sgl.query(ListBoats, many)
1-- sgl.query(ListBoats, many)
2SELECT id, name, berth, at_sea FROM boats ORDER BY berth;
2SELECT id, name, berth, at_sea FROM boats ORDER BY berth;
PutToSeadiffers
SQLite · db/queries/boats.sql
CockroachDB · db/queries/boats.sql
1-- sgl.query(PutToSea, execrows)
1-- sgl.query(PutToSea, execrows)
2UPDATE boats SET at_sea = 1 WHERE at_sea = 0;
2UPDATE boats SET at_sea = true WHERE NOT at_sea;
ScrapBoatdiffers
SQLite · db/queries/boats.sql
CockroachDB · db/queries/boats.sql
1-- sgl.query(ScrapBoat, exec)
1-- sgl.query(ScrapBoat, exec)
2DELETE FROM boats WHERE id = ?;
2DELETE FROM boats WHERE id = $1;
MoveBerthonly CockroachDB
SQLite · not in this version
CockroachDB · db/queries/boats.sql
1-- sgl.query(MoveBerth, execresult)
2UPDATE boats SET berth = $1 WHERE id = $2;

What sgl generates

MoorBoatdiffers
SQLite
CockroachDB
1func (queries *Queries) MoorBoat(ctx context.Context, params MoorBoatParams) (sql.Result, error)
1func (queries *Queries) MoorBoat(ctx context.Context, params MoorBoatParams) (MoorBoatRow, error)
2
2
3type MoorBoatParams struct {
3type MoorBoatParams struct {
4 Name string
4 Name string
5 Berth int64
5 Berth int64
6}
6}
7
8type MoorBoatRow struct {
9 ID int64 `json:"id"`
10}
GetBoatidentical
SQLite
CockroachDB
1func (queries *Queries) GetBoat(ctx context.Context, id int64) (GetBoatRow, error)
1func (queries *Queries) GetBoat(ctx context.Context, id int64) (GetBoatRow, error)
2
2
3type GetBoatRow struct {
3type GetBoatRow struct {
4 ID int64 `json:"id"`
4 ID int64 `json:"id"`
5 Name string `json:"name"`
5 Name string `json:"name"`
6 Berth int64 `json:"berth"`
6 Berth int64 `json:"berth"`
7 AtSea bool `json:"at_sea"`
7 AtSea bool `json:"at_sea"`
8}
8}
ListBoatsidentical
SQLite
CockroachDB
1func (queries *Queries) ListBoats(ctx context.Context) ([]ListBoatsRow, error)
1func (queries *Queries) ListBoats(ctx context.Context) ([]ListBoatsRow, error)
2
2
3type ListBoatsRow struct {
3type ListBoatsRow struct {
4 ID int64 `json:"id"`
4 ID int64 `json:"id"`
5 Name string `json:"name"`
5 Name string `json:"name"`
6 Berth int64 `json:"berth"`
6 Berth int64 `json:"berth"`
7 AtSea bool `json:"at_sea"`
7 AtSea bool `json:"at_sea"`
8}
8}
PutToSeaidentical
SQLite
CockroachDB
1func (queries *Queries) PutToSea(ctx context.Context) (int64, error)
1func (queries *Queries) PutToSea(ctx context.Context) (int64, error)
ScrapBoatidentical
SQLite
CockroachDB
1func (queries *Queries) ScrapBoat(ctx context.Context, id int64) error
1func (queries *Queries) ScrapBoat(ctx context.Context, id int64) error
MoveBerthonly CockroachDB
SQLite · not in this version
CockroachDB
1func (queries *Queries) MoveBerth(ctx context.Context, params MoveBerthParams) (sql.Result, error)
2
3type MoveBerthParams struct {
4 Berth int64
5 ID int64
6}

The program

example.godiffers
SQLite
CockroachDB
1package main
1package main
2
2
3import (
3import (
4 "context"
4 "context"
5 "database/sql"
5 "database/sql"
6 "errors"
6 "errors"
7 "fmt"
7 "fmt"
8 "io"
8 "io"
9
9
10 "seagull.sh/seagull/examples/sqlite/002_commands/internal/store"
10 "seagull.sh/seagull/examples/postgres/002_commands/internal/store"
11)
11)
12
12
13// run moors three boats, looks one up, scraps one, puts the rest to sea and lists the
13// run moors three boats, moves one, looks one up, scraps one, puts the rest to sea and
14// harbour. Each query in db/queries/boats.sql names a command, and the command decides
14// lists the harbour. Each query in db/queries/boats.sql names a command, and the command
15// what its generated method returns:
15// decides what its generated method returns:
16//
16//
17// - execresult returns sql.Result, which carries the new row's ID;
17// - one returns a single row, or sql.ErrNoRows when nothing matches; on an INSERT with
18// - one returns a single row, or sql.ErrNoRows when nothing matches;
18// RETURNING it hands back the new row's ID;
19// - execresult returns sql.Result. pgx's driver fills in RowsAffected, but its
20// LastInsertId always fails, as PostgreSQL has no last insert ID: use RETURNING;
19// - many returns a slice of rows;
21// - many returns a slice of rows;
20// - execrows returns how many rows the statement changed;
22// - execrows returns how many rows the statement changed;
21// - exec returns only an error.
23// - exec returns only an error.
22//
24//
23// The command has to fit the statement: exec on a SELECT, or one on an INSERT without
25// The command has to fit the statement: exec on a SELECT, or one on an INSERT without
24// RETURNING, makes sgl vet report Q009.
26// RETURNING, makes sgl vet report Q009.
25//
27//
26// Takes database (*sql.DB) which is a migrated database.
28// Takes database (*sql.DB) which is a migrated database.
27// Takes output (io.Writer) which receives what each call returned.
29// Takes output (io.Writer) which receives what each call returned.
28//
30//
29// Returns error when a query fails.
31// Returns error when a query fails.
30func run(ctx context.Context, database *sql.DB, output io.Writer) error {
32func run(ctx context.Context, database *sql.DB, output io.Writer) error {
31 queries := store.New(database)
33 queries := store.New(database)
32
34
33 arrivals := []store.MoorBoatParams{
35 arrivals := []store.MoorBoatParams{
34 {Name: "Kittiwake", Berth: 1},
36 {Name: "Kittiwake", Berth: 1},
35 {Name: "Salty Chip", Berth: 2},
37 {Name: "Salty Chip", Berth: 2},
36 {Name: "Gull Wing", Berth: 5},
38 {Name: "Gull Wing", Berth: 5},
37 }
39 }
38 for _, arrival := range arrivals {
40 for _, arrival := range arrivals {
39 result, err := queries.MoorBoat(ctx, arrival)
41 moored, err := queries.MoorBoat(ctx, arrival)
40 if err != nil {
42 if err != nil {
41 return fmt.Errorf("mooring %s: %w", arrival.Name, err)
43 return fmt.Errorf("mooring %s: %w", arrival.Name, err)
42 }
44 }
43 id, err := result.LastInsertId()
45 fmt.Fprintf(output, "one moored %s as boat %d\n", arrival.Name, moored.ID)
44 if err != nil {
45 return fmt.Errorf("reading %s's ID: %w", arrival.Name, err)
46 }
47 fmt.Fprintf(output, "execresult moored %s as boat %d\n", arrival.Name, id)
48 }
46 }
49
47
48 result, err := queries.MoveBerth(ctx, store.MoveBerthParams{Berth: 4, ID: 2})
49 if err != nil {
50 return fmt.Errorf("moving boat 2: %w", err)
51 }
52 moved, err := result.RowsAffected()
53 if err != nil {
54 return fmt.Errorf("reading the rows moved: %w", err)
55 }
56 fmt.Fprintf(output, "execresult moved boat 2 to berth 4, %d row affected\n", moved)
57 if _, err := result.LastInsertId(); err != nil {
58 fmt.Fprintf(output, "execresult LastInsertId fails: %v\n", err)
59 }
60
50 boat, err := queries.GetBoat(ctx, 2)
61 boat, err := queries.GetBoat(ctx, 2)
51 if err != nil {
62 if err != nil {
52 return fmt.Errorf("getting boat 2: %w", err)
63 return fmt.Errorf("getting boat 2: %w", err)
53 }
64 }
54 fmt.Fprintf(output, "one boat 2 is %s, at berth %d\n", boat.Name, boat.Berth)
65 fmt.Fprintf(output, "one boat 2 is %s, at berth %d\n", boat.Name, boat.Berth)
55
66
56 if err := queries.ScrapBoat(ctx, 3); err != nil {
67 if err := queries.ScrapBoat(ctx, 3); err != nil {
57 return fmt.Errorf("scrapping boat 3: %w", err)
68 return fmt.Errorf("scrapping boat 3: %w", err)
58 }
69 }
59 fmt.Fprintln(output, "exec scrapped boat 3")
70 fmt.Fprintln(output, "exec scrapped boat 3")
60
71
61 _, err = queries.GetBoat(ctx, 3)
72 _, err = queries.GetBoat(ctx, 3)
62 if err == nil {
73 if err == nil {
63 return errors.New("getting scrapped boat 3: got a boat, want sql.ErrNoRows")
74 return errors.New("getting scrapped boat 3: got a boat, want sql.ErrNoRows")
64 }
75 }
65 if !errors.Is(err, sql.ErrNoRows) {
76 if !errors.Is(err, sql.ErrNoRows) {
66 return fmt.Errorf("getting scrapped boat 3: got %w, want sql.ErrNoRows", err)
77 return fmt.Errorf("getting scrapped boat 3: got %w, want sql.ErrNoRows", err)
67 }
78 }
68 fmt.Fprintln(output, "one boat 3 is gone, so GetBoat returns sql.ErrNoRows")
79 fmt.Fprintln(output, "one boat 3 is gone, so GetBoat returns sql.ErrNoRows")
69
80
70 // PutToSea only changes boats still in harbour, so a second run changes none.
81 // PutToSea only changes boats still in harbour, so a second run changes none.
71 for range 2 {
82 for range 2 {
72 sailed, err := queries.PutToSea(ctx)
83 sailed, err := queries.PutToSea(ctx)
73 if err != nil {
84 if err != nil {
74 return fmt.Errorf("putting boats to sea: %w", err)
85 return fmt.Errorf("putting boats to sea: %w", err)
75 }
86 }
76 fmt.Fprintf(output, "execrows %d boats put to sea\n", sailed)
87 fmt.Fprintf(output, "execrows %d boats put to sea\n", sailed)
77 }
88 }
78
89
79 boats, err := queries.ListBoats(ctx)
90 boats, err := queries.ListBoats(ctx)
80 if err != nil {
91 if err != nil {
81 return fmt.Errorf("listing boats: %w", err)
92 return fmt.Errorf("listing boats: %w", err)
82 }
93 }
83 for _, boat := range boats {
94 for _, boat := range boats {
84 fmt.Fprintf(output, "many berth %d %-10s at sea: %t\n", boat.Berth, boat.Name, boat.AtSea)
95 fmt.Fprintf(output, "many berth %d %-10s at sea: %t\n", boat.Berth, boat.Name, boat.AtSea)
85 }
96 }
86 return nil
97 return nil
87}
98}