Parents with child slices: MySQL and MariaDB
MySQL and MariaDB run the same files for this example: the SQL and the Go are identical.
Configuration
sgl.yamlidentical
MySQL
MariaDB
1
version: 11
version: 12
2
3
databases:3
databases:4
- name: chippy4
- name: chippy5
dialect: mysql5
dialect: mysql6
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/storeMigrations
db/migrations/0001_orders.up.sqlidentical
MySQL
MariaDB
1
CREATE TABLE orders (1
CREATE TABLE orders (2
id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,2
id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,3
customer VARCHAR(50) NOT NULL3
customer VARCHAR(50) NOT NULL4
);4
);5
5
6
CREATE TABLE order_lines (6
CREATE TABLE order_lines (7
id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,7
id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,8
order_id BIGINT NOT NULL,8
order_id BIGINT NOT NULL,9
dish VARCHAR(50) NOT NULL,9
dish VARCHAR(50) NOT NULL,10
quantity INT NOT NULL,10
quantity INT NOT NULL,11
FOREIGN KEY (order_id) REFERENCES orders (id)11
FOREIGN KEY (order_id) REFERENCES orders (id)12
);12
);db/migrations/0001_orders.down.sqlidentical
MySQL
MariaDB
1
DROP TABLE order_lines;1
DROP TABLE order_lines;2
DROP TABLE orders;2
DROP TABLE orders;Queries
OpenOrderidentical
MySQL · db/queries/orders.sql
MariaDB · db/queries/orders.sql
1
-- sgl.query(OpenOrder, exec)1
-- sgl.query(OpenOrder, exec)2
INSERT INTO orders (customer) VALUES (?);2
INSERT INTO orders (customer) VALUES (?);AddLineidentical
MySQL · db/queries/orders.sql
MariaDB · db/queries/orders.sql
1
-- sgl.query(AddLine, exec)1
-- sgl.query(AddLine, exec)2
INSERT INTO order_lines (order_id, dish, quantity) VALUES (?, ?, ?);2
INSERT INTO order_lines (order_id, dish, quantity) VALUES (?, ?, ?);ListOrdersidentical
MySQL · db/queries/orders.sql
MariaDB · 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
MySQL
MariaDB
1
func (queries *Queries) OpenOrder(ctx context.Context, customer string) error1
func (queries *Queries) OpenOrder(ctx context.Context, customer string) errorAddLineidentical
MySQL
MariaDB
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 int327
}7
}ListOrdersidentical
MySQL
MariaDB
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 *int32 `json:"quantity"`17
}17
}The program
example.goidentical
MySQL
MariaDB
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/mysql/016_one_to_many/internal/store"9
"seagull.sh/seagull/examples/mysql/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 | MySQL | MariaDB |
|---|---|---|---|
| ListOrders | column id | *int64 | int64 |