Choosing a command: one, many, exec, execrows, execresult: CockroachDB and SQLite
Every file of the CockroachDB and SQLite versions side by side, with the lines that differ marked.
Configuration
sgl.yamldiffers
CockroachDB
SQLite
1
version: 11
version: 12
2
3
databases:3
databases:4
- name: harbour4
- name: harbour5
dialect: postgres5
dialect: sqlite6
migrations: db/migrations6
migrations: db/migrations7
queries: db/queries7
queries: db/queries8
go:8
go:9
package: store9
package: store10
out: internal/store10
out: internal/store11
driver: database/sqlMigrations
db/migrations/0001_boats.up.sqldiffers
CockroachDB
SQLite
1
CREATE TABLE boats (1
CREATE TABLE boats (2
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,2
id INTEGER PRIMARY KEY,3
name TEXT NOT NULL,3
name TEXT NOT NULL,4
berth BIGINT NOT NULL,4
berth INTEGER NOT NULL,5
at_sea BOOLEAN NOT NULL DEFAULT false5
at_sea BOOLEAN NOT NULL DEFAULT 06
);6
);db/migrations/0001_boats.down.sqlidentical
CockroachDB
SQLite
1
DROP TABLE boats;1
DROP TABLE boats;Queries
MoorBoatdiffers
CockroachDB · db/queries/boats.sql
SQLite · db/queries/boats.sql
1
-- sgl.query(MoorBoat, one)1
-- sgl.query(MoorBoat, execresult)2
INSERT INTO boats (name, berth) VALUES ($1, $2) RETURNING id;2
INSERT INTO boats (name, berth) VALUES (?, ?);MoveBerthonly CockroachDB
CockroachDB · db/queries/boats.sql
SQLite · not in this version
1
-- sgl.query(MoveBerth, execresult)2
UPDATE boats SET berth = $1 WHERE id = $2;GetBoatdiffers
CockroachDB · db/queries/boats.sql
SQLite · db/queries/boats.sql
1
-- sgl.query(GetBoat, one)1
-- sgl.query(GetBoat, one)2
SELECT id, name, berth, at_sea FROM boats WHERE id = $1;2
SELECT id, name, berth, at_sea FROM boats WHERE id = ?;ListBoatsidentical
CockroachDB · db/queries/boats.sql
SQLite · db/queries/boats.sql
1
-- sgl.query(ListBoats, many)1
-- sgl.query(ListBoats, many)2
SELECT id, name, berth, at_sea FROM boats ORDER BY berth;2
SELECT id, name, berth, at_sea FROM boats ORDER BY berth;PutToSeadiffers
CockroachDB · db/queries/boats.sql
SQLite · db/queries/boats.sql
1
-- sgl.query(PutToSea, execrows)1
-- sgl.query(PutToSea, execrows)2
UPDATE boats SET at_sea = true WHERE NOT at_sea;2
UPDATE boats SET at_sea = 1 WHERE at_sea = 0;ScrapBoatdiffers
CockroachDB · db/queries/boats.sql
SQLite · db/queries/boats.sql
1
-- sgl.query(ScrapBoat, exec)1
-- sgl.query(ScrapBoat, exec)2
DELETE FROM boats WHERE id = $1;2
DELETE FROM boats WHERE id = ?;What sgl generates
MoorBoatdiffers
CockroachDB
SQLite
1
func (queries *Queries) MoorBoat(ctx context.Context, params MoorBoatParams) (MoorBoatRow, error)1
func (queries *Queries) MoorBoat(ctx context.Context, params MoorBoatParams) (sql.Result, error)2
2
3
type MoorBoatParams struct {3
type MoorBoatParams struct {4
Name string4
Name string5
Berth int645
Berth int646
}6
}7
8
type MoorBoatRow struct {9
ID int64 `json:"id"`10
}MoveBerthonly CockroachDB
CockroachDB
SQLite · not in this version
1
func (queries *Queries) MoveBerth(ctx context.Context, params MoveBerthParams) (sql.Result, error)2
3
type MoveBerthParams struct {4
Berth int645
ID int646
}GetBoatidentical
CockroachDB
SQLite
1
func (queries *Queries) GetBoat(ctx context.Context, id int64) (GetBoatRow, error)1
func (queries *Queries) GetBoat(ctx context.Context, id int64) (GetBoatRow, error)2
2
3
type GetBoatRow struct {3
type 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
CockroachDB
SQLite
1
func (queries *Queries) ListBoats(ctx context.Context) ([]ListBoatsRow, error)1
func (queries *Queries) ListBoats(ctx context.Context) ([]ListBoatsRow, error)2
2
3
type ListBoatsRow struct {3
type 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
CockroachDB
SQLite
1
func (queries *Queries) PutToSea(ctx context.Context) (int64, error)1
func (queries *Queries) PutToSea(ctx context.Context) (int64, error)ScrapBoatidentical
CockroachDB
SQLite
1
func (queries *Queries) ScrapBoat(ctx context.Context, id int64) error1
func (queries *Queries) ScrapBoat(ctx context.Context, id int64) errorThe program
example.godiffers
CockroachDB
SQLite
1
package main1
package main2
2
3
import (3
import (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/postgres/002_commands/internal/store"10
"seagull.sh/seagull/examples/sqlite/002_commands/internal/store"11
)11
)12
12
13
// run moors three boats, moves one, looks one up, scraps one, puts the rest to sea and13
// run moors three boats, looks one up, scraps one, puts the rest to sea and lists the14
// lists the harbour. Each query in db/queries/boats.sql names a command, and the command14
// harbour. Each query in db/queries/boats.sql names a command, and the command decides15
// decides what its generated method returns:15
// what its generated method returns:16
//16
//17
// - one returns a single row, or sql.ErrNoRows when nothing matches; on an INSERT with17
// - execresult returns sql.Result, which carries the new row's ID;18
// RETURNING it hands back the new row's ID;18
// - one returns a single row, or sql.ErrNoRows when nothing matches;19
// - execresult returns sql.Result. pgx's driver fills in RowsAffected, but its20
// LastInsertId always fails, as PostgreSQL has no last insert ID: use RETURNING;21
// - many returns a slice of rows;19
// - many returns a slice of rows;22
// - execrows returns how many rows the statement changed;20
// - execrows returns how many rows the statement changed;23
// - exec returns only an error.21
// - exec returns only an error.24
//22
//25
// The command has to fit the statement: exec on a SELECT, or one on an INSERT without23
// The command has to fit the statement: exec on a SELECT, or one on an INSERT without26
// RETURNING, makes sgl vet report Q009.24
// RETURNING, makes sgl vet report Q009.27
//25
//28
// Takes database (*sql.DB) which is a migrated database.26
// Takes database (*sql.DB) which is a migrated database.29
// Takes output (io.Writer) which receives what each call returned.27
// Takes output (io.Writer) which receives what each call returned.30
//28
//31
// Returns error when a query fails.29
// Returns error when a query fails.32
func run(ctx context.Context, database *sql.DB, output io.Writer) error {30
func run(ctx context.Context, database *sql.DB, output io.Writer) error {33
queries := store.New(database)31
queries := store.New(database)34
32
35
arrivals := []store.MoorBoatParams{33
arrivals := []store.MoorBoatParams{36
{Name: "Kittiwake", Berth: 1},34
{Name: "Kittiwake", Berth: 1},37
{Name: "Salty Chip", Berth: 2},35
{Name: "Salty Chip", Berth: 2},38
{Name: "Gull Wing", Berth: 5},36
{Name: "Gull Wing", Berth: 5},39
}37
}40
for _, arrival := range arrivals {38
for _, arrival := range arrivals {41
moored, err := queries.MoorBoat(ctx, arrival)39
result, err := queries.MoorBoat(ctx, arrival)42
if err != nil {40
if err != nil {43
return fmt.Errorf("mooring %s: %w", arrival.Name, err)41
return fmt.Errorf("mooring %s: %w", arrival.Name, err)44
}42
}45
fmt.Fprintf(output, "one moored %s as boat %d\n", arrival.Name, moored.ID)43
id, err := result.LastInsertId()46
}44
if err != nil {47
45
return fmt.Errorf("reading %s's ID: %w", arrival.Name, err)48
result, err := queries.MoveBerth(ctx, store.MoveBerthParams{Berth: 4, ID: 2})46
}49
if err != nil {47
fmt.Fprintf(output, "execresult moored %s as boat %d\n", arrival.Name, id)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
}48
}60
49
61
boat, err := queries.GetBoat(ctx, 2)50
boat, err := queries.GetBoat(ctx, 2)62
if err != nil {51
if err != nil {63
return fmt.Errorf("getting boat 2: %w", err)52
return fmt.Errorf("getting boat 2: %w", err)64
}53
}65
fmt.Fprintf(output, "one boat 2 is %s, at berth %d\n", boat.Name, boat.Berth)54
fmt.Fprintf(output, "one boat 2 is %s, at berth %d\n", boat.Name, boat.Berth)66
55
67
if err := queries.ScrapBoat(ctx, 3); err != nil {56
if err := queries.ScrapBoat(ctx, 3); err != nil {68
return fmt.Errorf("scrapping boat 3: %w", err)57
return fmt.Errorf("scrapping boat 3: %w", err)69
}58
}70
fmt.Fprintln(output, "exec scrapped boat 3")59
fmt.Fprintln(output, "exec scrapped boat 3")71
60
72
_, err = queries.GetBoat(ctx, 3)61
_, err = queries.GetBoat(ctx, 3)73
if err == nil {62
if err == nil {74
return errors.New("getting scrapped boat 3: got a boat, want sql.ErrNoRows")63
return errors.New("getting scrapped boat 3: got a boat, want sql.ErrNoRows")75
}64
}76
if !errors.Is(err, sql.ErrNoRows) {65
if !errors.Is(err, sql.ErrNoRows) {77
return fmt.Errorf("getting scrapped boat 3: got %w, want sql.ErrNoRows", err)66
return fmt.Errorf("getting scrapped boat 3: got %w, want sql.ErrNoRows", err)78
}67
}79
fmt.Fprintln(output, "one boat 3 is gone, so GetBoat returns sql.ErrNoRows")68
fmt.Fprintln(output, "one boat 3 is gone, so GetBoat returns sql.ErrNoRows")80
69
81
// PutToSea only changes boats still in harbour, so a second run changes none.70
// PutToSea only changes boats still in harbour, so a second run changes none.82
for range 2 {71
for range 2 {83
sailed, err := queries.PutToSea(ctx)72
sailed, err := queries.PutToSea(ctx)84
if err != nil {73
if err != nil {85
return fmt.Errorf("putting boats to sea: %w", err)74
return fmt.Errorf("putting boats to sea: %w", err)86
}75
}87
fmt.Fprintf(output, "execrows %d boats put to sea\n", sailed)76
fmt.Fprintf(output, "execrows %d boats put to sea\n", sailed)88
}77
}89
78
90
boats, err := queries.ListBoats(ctx)79
boats, err := queries.ListBoats(ctx)91
if err != nil {80
if err != nil {92
return fmt.Errorf("listing boats: %w", err)81
return fmt.Errorf("listing boats: %w", err)93
}82
}94
for _, boat := range boats {83
for _, boat := range boats {95
fmt.Fprintf(output, "many berth %d %-10s at sea: %t\n", boat.Berth, boat.Name, boat.AtSea)84
fmt.Fprintf(output, "many berth %d %-10s at sea: %t\n", boat.Berth, boat.Name, boat.AtSea)96
}85
}97
return nil86
return nil98
}87
}