Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions mysql/mysql.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// Package mysql provides access to MySQL within Spin components.
package mysql

import (
Expand Down Expand Up @@ -114,9 +115,8 @@ type rows struct {
columns []string
columnType []uint8
pos int
len int
numRows int
rows [][]any
closed bool
}

var _ driver.Rows = (*rows)(nil)
Expand All @@ -132,8 +132,7 @@ func (r *rows) Columns() []string {
func (r *rows) Close() error {
r.rows = nil
r.pos = 0
r.len = 0
r.closed = true
r.numRows = 0
return nil
}

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

// NextResultSet advances the driver to the next result set even
Expand Down
2 changes: 2 additions & 0 deletions v3/examples/mysql-outbound/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
main.wasm
.spin/
30 changes: 30 additions & 0 deletions v3/examples/mysql-outbound/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Requirements
- Latest version of [TinyGo](https://tinygo.org/getting-started/)
- Latest version of [Docker](https://docs.docker.com/get-started/get-docker/)

# Usage

In a terminal window, use the below command to run MySQL:
```sh
docker compose up -d
```

Then, you'll build and run your Spin app:
```sh
spin up --build
```

In another terminal window, you can interact with the Spin app:
```sh
curl localhost:3000
```

You should see the output:
```json
[{"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}]
```

To stop and clean up the MySQL container, run the following:
```sh
docker compose down -v
```
24 changes: 24 additions & 0 deletions v3/examples/mysql-outbound/compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
services:
mysql:
image: mysql:8
container_name: mysql
restart: unless-stopped
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: spin_data
MYSQL_USER: spin
MYSQL_PASSWORD: spin
ports:
- "3306:3306"
volumes:
- mysql_data:/var/lib/mysql
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
networks:
- mysql_network

volumes:
mysql_data:

networks:
mysql_network:
driver: bridge
12 changes: 12 additions & 0 deletions v3/examples/mysql-outbound/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
module github.com/spinframework/spin-go-sdk/v3/examples/mysql-outbound

go 1.24

require github.com/spinframework/spin-go-sdk/v3 v3.0.0

require (
github.com/julienschmidt/httprouter v1.3.0 // indirect
go.bytecodealliance.org/cm v0.2.2 // indirect
)

replace github.com/spinframework/spin-go-sdk/v3 => ../../
4 changes: 4 additions & 0 deletions v3/examples/mysql-outbound/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
go.bytecodealliance.org/cm v0.2.2 h1:M9iHS6qs884mbQbIjtLX1OifgyPG9DuMs2iwz8G4WQA=
go.bytecodealliance.org/cm v0.2.2/go.mod h1:JD5vtVNZv7sBoQQkvBvAAVKJPhR/bqBH7yYXTItMfZI=
6 changes: 6 additions & 0 deletions v3/examples/mysql-outbound/init.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
CREATE DATABASE IF NOT EXISTS spin_data;
USE spin_data;
CREATE TABLE pets (id INT PRIMARY KEY, name VARCHAR(100) NOT NULL, prey VARCHAR(100), is_finicky BOOL NOT NULL);
INSERT INTO pets VALUES (1, 'Splodge', NULL, false);
INSERT INTO pets VALUES (2, 'Kiki', 'Cicadas', false);
INSERT INTO pets VALUES (3, 'Slats', 'Temptations', true);
58 changes: 58 additions & 0 deletions v3/examples/mysql-outbound/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package main

import (
"encoding/json"
"fmt"
"net/http"
"os"

spinhttp "github.com/spinframework/spin-go-sdk/v3/http"
"github.com/spinframework/spin-go-sdk/v3/mysql"
)

type Pet struct {
ID int64
Name string
Prey *string // nullable field must be a pointer
IsFinicky bool
}

func init() {
spinhttp.Handle(func(w http.ResponseWriter, r *http.Request) {

// addr is the environment variable set in `spin.toml` that points to the
// address of the Mysql server.
addr := os.Getenv("DB_URL")

db := mysql.Open(addr)
defer db.Close()

if _, err := db.Query("REPLACE INTO pets VALUES (?, 'Maya', ?, ?);", 4, "bananas", true); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}

if _, err := db.Exec("INSERT INTO pets VALUES (?, ?, ?, ?)", 5, "Copper", "Foxes", false); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}

rows, err := db.Query("SELECT * FROM pets")
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}

var pets []*Pet
for rows.Next() {
var pet Pet
if err := rows.Scan(&pet.ID, &pet.Name, &pet.Prey, &pet.IsFinicky); err != nil {
fmt.Println(err)
}
pets = append(pets, &pet)
}
json.NewEncoder(w).Encode(pets)
})
}

func main() {}
19 changes: 19 additions & 0 deletions v3/examples/mysql-outbound/spin.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
spin_manifest_version = 2

[application]
name = "go-mysql-outbound-example"
version = "0.1.0"
authors = ["Andrew Steurer <[email protected]>"]
description = "Using Spin with MySQL"

[[trigger.http]]
route = "/..."
component = "mysql"

[component.mysql]
environment = { DB_URL = "mysql://spin:[email protected]/spin_data" }
source = "main.wasm"
allowed_outbound_hosts = ["mysql://127.0.0.1"]
[component.mysql.build]
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"
watch = ["**/*.go", "go.mod"]
15 changes: 15 additions & 0 deletions v3/internal/db/driver.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package db

import "database/sql/driver"

// GlobalParameterConverter is a global valueConverter instance to convert parameters.
var GlobalParameterConverter = &valueConverter{}

var _ driver.ValueConverter = (*valueConverter)(nil)

// valueConverter is a no-op value converter.
type valueConverter struct{}

func (c *valueConverter) ConvertValue(v any) (driver.Value, error) {
return driver.Value(v), nil
}
Loading