Using UUID in Go with MySQL & Postgres Examples

Using UUID in Go with MySQL & Postgres Examples

A UUID is 128 bits long identifier and is intended to guarantee uniqueness across space and time. The format is described in RFC 9562. The most common versions are:

  • UUIDv4 - which are generated in random or pseudorandom order
  • UUIDv7 - which are are ordered by time generated

In Go there is a library google/uuid which generates the numbers according to RFC specification.

MySQL with UUIDv4

In MySQL, you can store UUID as BINARY(16). There are two database helper functions UUID_TO_BIN and inverse BIN_TO_UUID.

In following example, I am creating two users with UUID identifiers created in client code. Next I am fetching those users to User struct.

package main  
  
import (  
    "fmt"  
  
    _ "github.com/go-sql-driver/mysql"  
    "github.com/google/uuid"    
    "github.com/jmoiron/sqlx"
)  
  
type User struct {  
    ID   uuid.UUID  
    Name string  
}  
  
func main() {  
	// connect to database
    db, err := sqlx.Open("mysql", "root@tcp(127.0.0.1:3306)/test")  
    if err != nil {  
       panic(err)  
    }  

    // create schema
    db.MustExec(`DROP TABLE IF EXISTS users;`)  
    db.MustExec(`CREATE TABLE users (
    id BINARY(16) PRIMARY KEY,
	name VARCHAR(255) NOT NULL);`)  

	// insert two records
    db.MustExec("INSERT INTO users (id, name) VALUES (UUID_TO_BIN(?), ?)", uuid.New().String(), "John Doe")  
    db.MustExec("INSERT INTO users (id, name) VALUES (UUID_TO_BIN(?), ?)", uuid.New().String(), "John Smith")  

	// get users
    var users []User  
    err = db.Select(&users, "SELECT * FROM users")  
    if err != nil {  
       panic(err)  
    }  

	// print result
    fmt.Printf("%v\n", users)  
}

Output. The ids are in random order

[{997b6e94-0e99-4421-8a1d-09f0547c09a5 John Smith} {2b8030e5-5376-4d89-ae6c-5a351c60f8b7 John Doe}]

Postgres with UUIDv7

package main  
  
import (  
    "fmt"  
  
    "github.com/google/uuid"
    "github.com/jmoiron/sqlx"
    _ "github.com/lib/pq"  
)  
  
type Article struct {  
    ID   uuid.UUID  
    Name string  
}  
  
func main() {  
    // connect to database
    // you can use following docker run command to start postgres
    // docker run --rm --name my-postgres --env POSTGRES_PASSWORD=admin --publish 5432:5432  postgres
    db, err := sqlx.Open("postgres", "user=postgres password=admin dbname=postgres sslmode=disable")  
    if err != nil {  
       panic(err)  
    }  
  
    // create schema  
    db.MustExec(`DROP TABLE IF EXISTS articles;`)  
    db.MustExec(`CREATE TABLE articles (  
       id uuid PRIMARY KEY,       name VARCHAR NOT NULL);`)  
  
    // insert two articles to the table  
    id, err := uuid.NewV7()  
    if err != nil {  
       panic(err)  
    }  
    db.MustExec("INSERT INTO articles (id, name) VALUES ($1, $2)", id.String(), "Intro to Go")  
  
    id2, err := uuid.NewV7()  
    if err != nil {  
       panic(err)  
    }  
    db.MustExec("INSERT INTO articles (id, name) VALUES ($1, $2)", id2.String(), "Intro to sqlx")  
  
    // get the articles  
    var articles []Article  
    err = db.Select(&articles, "SELECT * FROM articles")  
    if err != nil {  
       panic(err)  
    }  
  
    // print results  
    fmt.Printf("%v\n", articles)  
}

Output. The ids are time-sorted

[{019325f1-cd9e-73a9-a280-454c395a2668 Intro to Go} {019325f1-cda0-770c-a8e5-d4c104aecb5c Intro to sqlx}]