Parents with child slices: DuckDB and CockroachDB
Every file of the DuckDB and CockroachDB versions side by side, with the lines that differ marked. 3 parameter or column types differ in Go.
Configuration
sgl.yamldiffers
DuckDB
CockroachDB
1
version: 11
version: 12
2
3
databases:3
databases:4
- name: chippy4
- name: chippy5
dialect: duckdb5
dialect: postgres6
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/sql12
migrate:13
dsn: ${env:DATABASE_URL}Migrations
db/migrations/0001_orders.up.sqldiffers
DuckDB
CockroachDB
1
CREATE SEQUENCE order_ids;2
3
CREATE SEQUENCE order_line_ids;4
5
CREATE TABLE orders (1
CREATE TABLE orders (6
id BIGINT PRIMARY KEY DEFAULT nextval('order_ids'),2
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,7
customer VARCHAR NOT NULL3
customer TEXT NOT NULL8
);4
);9
5
10
CREATE TABLE order_lines (6
CREATE TABLE order_lines (11
id BIGINT PRIMARY KEY DEFAULT nextval('order_line_ids'),7
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,12
order_id BIGINT NOT NULL REFERENCES orders (id),8
order_id BIGINT NOT NULL REFERENCES orders (id),13
dish VARCHAR NOT NULL,9
dish TEXT NOT NULL,14
quantity INTEGER NOT NULL10
quantity BIGINT NOT NULL15
);11
);db/migrations/0001_orders.down.sqldiffers
DuckDB
CockroachDB
1
DROP TABLE order_lines;1
DROP TABLE order_lines;2
DROP TABLE orders;2
DROP TABLE orders;3
DROP SEQUENCE order_line_ids;4
DROP SEQUENCE order_ids;Queries
OpenOrderidentical
DuckDB · db/queries/orders.sql
CockroachDB · db/queries/orders.sql
1
-- sgl.query(OpenOrder, exec)1
-- sgl.query(OpenOrder, exec)2
INSERT INTO orders (customer) VALUES ($1);2
INSERT INTO orders (customer) VALUES ($1);AddLineidentical
DuckDB · db/queries/orders.sql
CockroachDB · db/queries/orders.sql
1
-- sgl.query(AddLine, exec)1
-- sgl.query(AddLine, exec)2
INSERT INTO order_lines (order_id, dish, quantity) VALUES ($1, $2, $3);2
INSERT INTO order_lines (order_id, dish, quantity) VALUES ($1, $2, $3);ListOrdersidentical
DuckDB · db/queries/orders.sql
CockroachDB · db/queries/orders.sql
1
-- sgl.query(ListOrders, many, group_by: orders.id)1
-- sgl.query(ListOrders, many, group_by: orders.id)2
-- sgl.embed(orders)2
-- sgl.embed(orders)3
-- sgl.embed(order_lines)3
-- sgl.embed(order_lines)4
SELECT orders.id, orders.customer, order_lines.id, order_lines.dish, order_lines.quantity4
SELECT orders.id, orders.customer, order_lines.id, order_lines.dish, order_lines.quantity5
FROM orders5
FROM orders6
LEFT JOIN order_lines ON order_lines.order_id = orders.id6
LEFT JOIN order_lines ON order_lines.order_id = orders.id7
ORDER BY orders.id, order_lines.id;7
ORDER BY orders.id, order_lines.id;What sgl generates
OpenOrderidentical
DuckDB
CockroachDB
1
func (queries *Queries) OpenOrder(ctx context.Context, customer string) error1
func (queries *Queries) OpenOrder(ctx context.Context, customer string) errorAddLinediffers
DuckDB
CockroachDB
1
func (queries *Queries) AddLine(ctx context.Context, params AddLineParams) error1
func (queries *Queries) AddLine(ctx context.Context, params AddLineParams) error2
2
3
type AddLineParams struct {3
type AddLineParams struct {4
OrderID int644
OrderID int645
Dish string5
Dish string6
Quantity int326
Quantity int647
}7
}ListOrdersdiffers
DuckDB
CockroachDB
1
func (queries *Queries) ListOrders(ctx context.Context) ([]ListOrdersRow, error)1
func (queries *Queries) ListOrders(ctx context.Context) ([]ListOrdersRow, error)2
2
3
type ListOrdersRow struct {3
type ListOrdersRow struct {4
Orders ListOrdersOrders4
Orders ListOrdersOrders5
OrderLines []ListOrdersOrderLines5
OrderLines []ListOrdersOrderLines6
}6
}7
7
8
type ListOrdersOrders struct {8
type ListOrdersOrders struct {9
ID int64 `json:"id"`9
ID int64 `json:"id"`10
Customer string `json:"customer"`10
Customer string `json:"customer"`11
}11
}12
12
13
type ListOrdersOrderLines struct {13
type ListOrdersOrderLines struct {14
ID *int64 `json:"id"`14
ID *int64 `json:"id"`15
Dish *string `json:"dish"`15
Dish *string `json:"dish"`16
Quantity *int32 `json:"quantity"`16
Quantity *int64 `json:"quantity"`17
}17
}The program
example.godiffers
DuckDB
CockroachDB
1
package main1
package main2
2
3
import (3
import (4
"context"4
"context"5
"database/sql"5
"database/sql"6
"fmt"6
"fmt"7
"io"7
"io"8
8
9
"seagull.sh/seagull/examples/duckdb/016_one_to_many/internal/store"9
"seagull.sh/seagull/examples/postgres/016_one_to_many/internal/store"10
)10
)11
11
12
// run takes three chip shop orders and lists them with their lines. The join in12
// run takes three chip shop orders and lists them with their lines. The join in13
// ListOrders returns one row per order line, repeating the order's columns each time.13
// ListOrders returns one row per order line, repeating the order's columns each time.14
// group_by: orders.id collapses those rows: each distinct orders.id becomes one14
// group_by: orders.id collapses those rows: each distinct orders.id becomes one15
// ListOrdersRow, holding the order's columns in Orders and every matching line in the15
// ListOrdersRow, holding the order's columns in Orders and every matching line in the16
// OrderLines slice. Groups keep the order the rows arrive in.16
// OrderLines slice. Groups keep the order the rows arrive in.17
//17
//18
// It is a LEFT JOIN, so an order with no lines still appears, with an empty slice, and18
// It is a LEFT JOIN, so an order with no lines still appears, with an empty slice, and19
// the line fields are pointers. sgl vet checks the directive: group_by must name a column19
// the line fields are pointers. sgl vet checks the directive: group_by must name a column20
// the query returns (Q014), the other tables need sgl.embed (Q015), and the command must20
// the query returns (Q014), the other tables need sgl.embed (Q015), and the command must21
// be many (Q016).21
// be many (Q016).22
//22
//23
// Takes database (*sql.DB) which is a migrated database.23
// Takes database (*sql.DB) which is a migrated database.24
// Takes output (io.Writer) which receives each order and its lines.24
// Takes output (io.Writer) which receives each order and its lines.25
//25
//26
// Returns error when a query fails.26
// Returns error when a query fails.27
func run(ctx context.Context, database *sql.DB, output io.Writer) error {27
func run(ctx context.Context, database *sql.DB, output io.Writer) error {28
queries := store.New(database)28
queries := store.New(database)29
29
30
for _, customer := range []string{"Steven", "Gwen", "Jago"} {30
for _, customer := range []string{"Steven", "Gwen", "Jago"} {31
if err := queries.OpenOrder(ctx, customer); err != nil {31
if err := queries.OpenOrder(ctx, customer); err != nil {32
return fmt.Errorf("opening %s's order: %w", customer, err)32
return fmt.Errorf("opening %s's order: %w", customer, err)33
}33
}34
}34
}35
lines := []store.AddLineParams{35
lines := []store.AddLineParams{36
{OrderID: 1, Dish: "cod and chips", Quantity: 2},36
{OrderID: 1, Dish: "cod and chips", Quantity: 2},37
{OrderID: 1, Dish: "mushy peas", Quantity: 1},37
{OrderID: 1, Dish: "mushy peas", Quantity: 1},38
{OrderID: 1, Dish: "pickled egg", Quantity: 3},38
{OrderID: 1, Dish: "pickled egg", Quantity: 3},39
{OrderID: 2, Dish: "chips", Quantity: 1},39
{OrderID: 2, Dish: "chips", Quantity: 1},40
}40
}41
for _, line := range lines {41
for _, line := range lines {42
if err := queries.AddLine(ctx, line); err != nil {42
if err := queries.AddLine(ctx, line); err != nil {43
return fmt.Errorf("adding %s to order %d: %w", line.Dish, line.OrderID, err)43
return fmt.Errorf("adding %s to order %d: %w", line.Dish, line.OrderID, err)44
}44
}45
}45
}46
46
47
orders, err := queries.ListOrders(ctx)47
orders, err := queries.ListOrders(ctx)48
if err != nil {48
if err != nil {49
return fmt.Errorf("listing orders: %w", err)49
return fmt.Errorf("listing orders: %w", err)50
}50
}51
for _, order := range orders {51
for _, order := range orders {52
fmt.Fprintf(output, "order %d for %s\n", order.Orders.ID, order.Orders.Customer)52
fmt.Fprintf(output, "order %d for %s\n", order.Orders.ID, order.Orders.Customer)53
if len(order.OrderLines) == 0 {53
if len(order.OrderLines) == 0 {54
fmt.Fprintln(output, " nothing ordered yet")54
fmt.Fprintln(output, " nothing ordered yet")55
}55
}56
for _, line := range order.OrderLines {56
for _, line := range order.OrderLines {57
fmt.Fprintf(output, " %d x %s\n", *line.Quantity, *line.Dish)57
fmt.Fprintf(output, " %d x %s\n", *line.Quantity, *line.Dish)58
}58
}59
}59
}60
return nil60
return nil61
}61
}Go type differences
The same SQL types map to different Go types, because each database reports them differently.
| Query | Field | DuckDB | CockroachDB |
|---|---|---|---|
| AddLine | parameter quantity | int32 | int64 |
| ListOrders | column id | *int64 | int64 |
| ListOrders | column quantity | *int32 | *int64 |