Skip to content

Commit a52a86a

Browse files
authored
Merge pull request #31 from asteurer/wasip2-mysql
feat(mysql): implementing mysql in wasip2
2 parents aaeb45a + b616203 commit a52a86a

File tree

11 files changed

+516
-5
lines changed

11 files changed

+516
-5
lines changed

mysql/mysql.go

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// Package mysql provides access to MySQL within Spin components.
12
package mysql
23

34
import (
@@ -114,9 +115,8 @@ type rows struct {
114115
columns []string
115116
columnType []uint8
116117
pos int
117-
len int
118+
numRows int
118119
rows [][]any
119-
closed bool
120120
}
121121

122122
var _ driver.Rows = (*rows)(nil)
@@ -132,8 +132,7 @@ func (r *rows) Columns() []string {
132132
func (r *rows) Close() error {
133133
r.rows = nil
134134
r.pos = 0
135-
r.len = 0
136-
r.closed = true
135+
r.numRows = 0
137136
return nil
138137
}
139138

@@ -152,7 +151,7 @@ func (r *rows) Next(dest []driver.Value) error {
152151
// HasNextResultSet is called at the end of the current result set and
153152
// reports whether there is another result set after the current one.
154153
func (r *rows) HasNextResultSet() bool {
155-
return r.pos < r.len
154+
return r.pos < r.numRows
156155
}
157156

158157
// NextResultSet advances the driver to the next result set even

v3/examples/mysql-outbound/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
main.wasm
2+
.spin/

v3/examples/mysql-outbound/README.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Requirements
2+
- Latest version of [TinyGo](https://tinygo.org/getting-started/)
3+
- Latest version of [Docker](https://docs.docker.com/get-started/get-docker/)
4+
5+
# Usage
6+
7+
In a terminal window, use the below command to run MySQL:
8+
```sh
9+
docker compose up -d
10+
```
11+
12+
Then, you'll build and run your Spin app:
13+
```sh
14+
spin up --build
15+
```
16+
17+
In another terminal window, you can interact with the Spin app:
18+
```sh
19+
curl localhost:3000
20+
```
21+
22+
You should see the output:
23+
```json
24+
[{"ID":1,"Name":"Splodge","Prey":null,"IsFinicky":false},{"ID":2,"Name":"Kiki","Prey":"Cicadas","IsFinicky":false},{"ID":3,"Name":"Slats","Prey":"Temptations","IsFinicky":true},{"ID":4,"Name":"Maya","Prey":"bananas","IsFinicky":true},{"ID":5,"Name":"Copper","Prey":"Foxes","IsFinicky":false}]
25+
```
26+
27+
To stop and clean up the MySQL container, run the following:
28+
```sh
29+
docker compose down -v
30+
```
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
services:
2+
mysql:
3+
image: mysql:8
4+
container_name: mysql
5+
restart: unless-stopped
6+
environment:
7+
MYSQL_ROOT_PASSWORD: root
8+
MYSQL_DATABASE: spin_data
9+
MYSQL_USER: spin
10+
MYSQL_PASSWORD: spin
11+
ports:
12+
- "3306:3306"
13+
volumes:
14+
- mysql_data:/var/lib/mysql
15+
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
16+
networks:
17+
- mysql_network
18+
19+
volumes:
20+
mysql_data:
21+
22+
networks:
23+
mysql_network:
24+
driver: bridge

v3/examples/mysql-outbound/go.mod

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
module github.com/spinframework/spin-go-sdk/v3/examples/mysql-outbound
2+
3+
go 1.24
4+
5+
require github.com/spinframework/spin-go-sdk/v3 v3.0.0
6+
7+
require (
8+
github.com/julienschmidt/httprouter v1.3.0 // indirect
9+
go.bytecodealliance.org/cm v0.2.2 // indirect
10+
)
11+
12+
replace github.com/spinframework/spin-go-sdk/v3 => ../../

v3/examples/mysql-outbound/go.sum

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
2+
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
3+
go.bytecodealliance.org/cm v0.2.2 h1:M9iHS6qs884mbQbIjtLX1OifgyPG9DuMs2iwz8G4WQA=
4+
go.bytecodealliance.org/cm v0.2.2/go.mod h1:JD5vtVNZv7sBoQQkvBvAAVKJPhR/bqBH7yYXTItMfZI=

v3/examples/mysql-outbound/init.sql

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
CREATE DATABASE IF NOT EXISTS spin_data;
2+
USE spin_data;
3+
CREATE TABLE pets (id INT PRIMARY KEY, name VARCHAR(100) NOT NULL, prey VARCHAR(100), is_finicky BOOL NOT NULL);
4+
INSERT INTO pets VALUES (1, 'Splodge', NULL, false);
5+
INSERT INTO pets VALUES (2, 'Kiki', 'Cicadas', false);
6+
INSERT INTO pets VALUES (3, 'Slats', 'Temptations', true);

v3/examples/mysql-outbound/main.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package main
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"net/http"
7+
"os"
8+
9+
spinhttp "github.com/spinframework/spin-go-sdk/v3/http"
10+
"github.com/spinframework/spin-go-sdk/v3/mysql"
11+
)
12+
13+
type Pet struct {
14+
ID int64
15+
Name string
16+
Prey *string // nullable field must be a pointer
17+
IsFinicky bool
18+
}
19+
20+
func init() {
21+
spinhttp.Handle(func(w http.ResponseWriter, r *http.Request) {
22+
23+
// addr is the environment variable set in `spin.toml` that points to the
24+
// address of the Mysql server.
25+
addr := os.Getenv("DB_URL")
26+
27+
db := mysql.Open(addr)
28+
defer db.Close()
29+
30+
if _, err := db.Query("REPLACE INTO pets VALUES (?, 'Maya', ?, ?);", 4, "bananas", true); err != nil {
31+
http.Error(w, err.Error(), http.StatusInternalServerError)
32+
return
33+
}
34+
35+
if _, err := db.Exec("INSERT INTO pets VALUES (?, ?, ?, ?)", 5, "Copper", "Foxes", false); err != nil {
36+
http.Error(w, err.Error(), http.StatusInternalServerError)
37+
return
38+
}
39+
40+
rows, err := db.Query("SELECT * FROM pets")
41+
if err != nil {
42+
http.Error(w, err.Error(), http.StatusInternalServerError)
43+
return
44+
}
45+
46+
var pets []*Pet
47+
for rows.Next() {
48+
var pet Pet
49+
if err := rows.Scan(&pet.ID, &pet.Name, &pet.Prey, &pet.IsFinicky); err != nil {
50+
fmt.Println(err)
51+
}
52+
pets = append(pets, &pet)
53+
}
54+
json.NewEncoder(w).Encode(pets)
55+
})
56+
}
57+
58+
func main() {}

v3/examples/mysql-outbound/spin.toml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
spin_manifest_version = 2
2+
3+
[application]
4+
name = "go-mysql-outbound-example"
5+
version = "0.1.0"
6+
authors = ["Andrew Steurer <[email protected]>"]
7+
description = "Using Spin with MySQL"
8+
9+
[[trigger.http]]
10+
route = "/..."
11+
component = "mysql"
12+
13+
[component.mysql]
14+
environment = { DB_URL = "mysql://spin:[email protected]/spin_data" }
15+
source = "main.wasm"
16+
allowed_outbound_hosts = ["mysql://127.0.0.1"]
17+
[component.mysql.build]
18+
command = "tinygo build -target=wasip2 --wit-package $(go list -mod=readonly -m -f '{{.Dir}}' github.com/spinframework/spin-go-sdk/v3)/wit --wit-world http-trigger -gc=leaking -o main.wasm main.go"
19+
watch = ["**/*.go", "go.mod"]

v3/internal/db/driver.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package db
2+
3+
import "database/sql/driver"
4+
5+
// GlobalParameterConverter is a global valueConverter instance to convert parameters.
6+
var GlobalParameterConverter = &valueConverter{}
7+
8+
var _ driver.ValueConverter = (*valueConverter)(nil)
9+
10+
// valueConverter is a no-op value converter.
11+
type valueConverter struct{}
12+
13+
func (c *valueConverter) ConvertValue(v any) (driver.Value, error) {
14+
return driver.Value(v), nil
15+
}

0 commit comments

Comments
 (0)