# README
go-database
Personal project.
go-database
is a library made for mysql
which provides a set of extensions on top of jmoiron/sqlx
such as a querybuilder, profiler, context & transactions for performances. This is not an ORM.
I plan to support pgsql
in the future.
➡ features
- Opening connection with environment variables :
- Global variables
DATABASE_*
- Aliases variables for multiple connection
DATABASE_ALIAS_*
- Global variables
- Opening connection with Environ
- A pool to manage all connections opened
- Transactions
- Profiling & Context
- Log out queries as string (formated) ordered by execution time grouped by context to detect slow queries.
Example : You can profile an application that uses goroutines such as webserver.
- Log out queries as string (formated) ordered by execution time grouped by context to detect slow queries.
- Query builder for complex query at the SQL layer
- This is not an ORM (yet)
➡ install
go get github.com/kovacou/go-database
➡ usage
Below is an example which shows some common use cases for go-database.
# .env file configuration (default settings)
# You can use DSN
DATABASE_DSN=
# Or use specfic variables
DATABASE_DRIVER=mysql
DATABASE_HOST=172.18.0.1
DATABASE_USER=test
DATABASE_PASS=test
DATABASE_SCHEMA=dbtest
DATABASE_PORT=3306
package main
import (
"log"
"github.com/kovacou/go-database"
b "github.com/kovacou/go-database/builder"
)
type User struct {
ID uint64
Name string
}
func main() {
// Opening a new connection based on environment variables.
// By default, the connection is postponed until there is an action.
db, err := database.Open()
if err != nil {
log.Fatal(err)
}
// Example of builder
s := b.Select{
Table: "users",
Where: b.ParseWhere("id < ?", 10)
}
println(s.String()) // SELECT * FROM users WHERE id < ?
// Mapping through a map
// (faster than structscan if you look for performance)
out := []User{}
db.SelectMap(&s, func(values map[string]any){
out = append(out, User{
ID: values["id"].(int64),
Name: string(values["name"].([]byte)),
})
})
// More faster than previous one.
out2 := []User{}
db.SelectSlice(&s, func(values []any) {
out2 = append(out2, User{
ID: values[0].(int64),
Name: string(values[1].([]byte)),
})
})
// Using raw query
out3 := []User{}
db.QuerySlice(builder.NewQuery("SELECT * FROM users WHERE id < ?", 10), func(values []any) {
out3 = append(out3, User{
// ...
})
})
// Using upsert
i := builder.Insert{
Table: "users",
Values: builder.H{
"id": 1,
"name": "John",
},
OnUpdateKeys: builder.Keys{"name"},
}
db.Exec(&i)
}
➡ opening a new connection
With environment variables
With environ
➡ closing all connections
func main() {
// Defering the close in your main ensure closing
// the connection before exiting the program.
defer database.Close()
// your code...
}
➡ transactions
Support of transactions.
// tx, err := db.Tx()
tx, err := db.Tx(sql.LevelSerializable)
if err != nil {
panic(err)
}
// use tx to run some requests.
tx.Commit()
tx.Rollback()
// You can't use tx anymore, else an error will occur.
➡ profiling & context
➡ statements
Select
s := builder.Select{
Table: "users",
Columns: builder.ParseColumns("id", "name"),
Where: builder.ParseWhere("id > ?", 1),
OrderBy: builder.ParseOrderBy("name ASC"),
}
Map
The columns can be read by key name.
// Parse 1 row only.
{
out := User{}
n, err := db.SelectMapRow(&s, func(v map[string]any) {
// If there is more than 1 row, an error occur.
// `n` return 0 or 1.
outRow.ID = v["id"].(int64)
outRow.Name = string(v["name"].([]byte))
})
}
// Parse multiple rows.
{
out := []User{}
n, err := db.SelectMap(&s, func(v map[string]any) {
// `n` contains the number of rows returned.
out = append(out, User{
ID: v["id"].(int64),
Name: string(v["name"].([]byte)),
})
})
}
Slice
The columns can be read by indexes from the Column clause (same order).
Note: Slice is faster than Map. Prefer use Slice when the columns have always the same order.
// Parse 1 row only
{
out := User{}
n, err := db.SelectSliceRow(&s, func(v []any){
// If there is more than1 row, an error occur.
// `n` return 0 or 1
out.ID = v[0].(int64)
out.Name = string(v[1].([]byte))
})
}
// Parse multiple rows
{
out := []User{}
n, err := db.SelectSlice(&s, func(v []any){
out = append(out, User{
ID: v[0].(int64),
Name: string(v[1].([]byte)),
})
})
}
Exec
Insert
// Example of Insert
i := builder.Insert{
Table: "users",
IgnoreMode: false, // False by default
Values: builder.H{
"name": "John",
},
}
println(i.String()) // INSERT INTO users(name) VALUES(?)
r, err := db.Exec(&i)
Upsert
// Example of Insert with upsert mode.
i := builder.Insert{
Table: "users",
Values: builder.H{
"id": 15,
"name": "John",
},
OnUpdateKeys: builder.Keys{"name"},
}
println(i.String()) // INSERT INTO users(id, name) VALUES(?, ?)
// ON DUPLICATE KEY UPDATE name = VALUES(name)
r, err := db.Exec(&i)
Update
// Example of Update
u := builder.Update{
Table: "users",
Values: builder.H{
"name": "John",
},
Where: builder.ParseWhere("id = ?", 1) // Used to initiate the value (if needed)
// To initiate empty value :
// builder.NewWhere()
}
// You can also use where like following :
// u.Where.And("id = ?", 1)
println(u.String()) // UPDATE users SET name = ? WHERE id = ?
r, err := db.Exec(&u)
Delete
// Example of Delete
d := builder.Delete{
Table: "users",
Where: builder.ParseWhere("id = ?", 1) // Used to initiate the value (if needed)
// To initiate empty value :
// builder.NewWhere()
}
// You can also use where like following :
// d.Where.And("id = ?", 1)
println(d.String()) // DELETE FROM users WHERE id = ?
r, err := db.Exec(&d)
# Packages
No description provided by the author
# Functions
Close closes active connections in the pool.
Open opens a database from default environement.
OpenEnv opens a database from given environment.
OpenEnviron opens a database from a given environ.
OpenOnce opens a new connection or return the existing one.
OpenOnceEnv opens a database from a given environment or return an existing one.
OpenOnceEnviron opens a database from a given environ or return existing one.
OpenWith opens a database with given connection.
# Variables
Debug is a global mode, when set at true, it will override the configuration of the connections.
IsolationLevel is the default isolation level.
Verbose is a global mode, when set at true, it will override the configuration of the connections.
# Structs
Environment store the configuration to open a new connection.
# Interfaces
No description provided by the author
Context is the interface that abstract all interaction of the connection.
QueryState is the interface that abstract the state of the execution of an query.
Stmt is the representation of an statement or query (SELECT, UPDATE, & DELETE).
# Type aliases
ContextFunc handler.
No description provided by the author
No description provided by the author
TxFunc handler.