gopsql is a simple tool to make PostgreSQL database queries, especially for developing RESTful API with JSON. Some features are learned from Rails.
From furk.
- ✅ Model is a database table and it is created from struct. Column names are inferred from struct field names. You can write less SQL statements for CRUD operations.
- ✅ Support JSONB data type, you can store many fields in one jsonb column, like Rails' store_accessor.
- ✅ Safely insert and update record with Filter() to filter unwanted data, like Rails' permit.
- ✅ Migrate your database like Rails database migrations.
- ✅ Support pq, pgx and go-pg, you can switch driver at runtime.
For more use cases, see Examples or Tests.
You can choose one of three PostgreSQL drivers (pq, pgx, gopq) to use at runtime:
- github.com/lib/pq v1.9.0
- github.com/jackc/pgx v4.10.1
- github.com/go-pg/pg v10.9.0
// import "github.com/gopsql/db"
// import "github.com/gopsql/gopg"
// import "github.com/gopsql/pgx"
// import "github.com/gopsql/pq"
connStr := "postgres://localhost:5432/gopsql?sslmode=disable"
connDrv := "gopg"
var conn db.DB
if connDrv == "pgx" {
conn = pgx.MustOpen(connStr)
} else if connDrv == "gopg" {
conn = gopg.MustOpen(connStr)
} else {
conn = pq.MustOpen(connStr)
}
defer conn.Close()
var name string
conn.QueryRow("SELECT current_database()").Scan(&name)
fmt.Println(name) // gopsql
If you don't want too much entries being inserted into your go.sum file, you can use lib/pq:
// import "database/sql"
// import "github.com/gopsql/standard"
// import _ "github.com/lib/pq"
c, err := sql.Open("postgres", "postgres://localhost:5432/gopsql?sslmode=disable")
if err != nil {
panic(err)
}
conn := &standard.DB{c}
defer conn.Close()
var name string
conn.QueryRow("SELECT current_database()").Scan(&name)
fmt.Println(name) // gopsql
Select 100 rows from database using different drivers, compared to their native
usages. You can run cd tests && GENERATE=1 go test -v ./benchmark_test.go
to
make this benchmark chart. For more information, see
Benchmark.
// type (
// Post struct {
// Id int
// CategoryId int
// Title string
// Picture string `jsonb:"meta"`
// }
// )
m := psql.NewModel(Post{}, conn, logger.StandardLogger)
Table name is inferred from the name of the struct, the tag of __TABLE_NAME__
field or its TableName() string
receiver. Column names are inferred from
struct field names or theirs "column" tags. Both table names and field names
are in snake case by default.
// CREATE TABLE users (
// id SERIAL PRIMARY KEY,
// category_id bigint DEFAULT 0 NOT NULL,
// status text DEFAULT ''::text NOT NULL,
// meta jsonb
// )
m.NewSQL(m.Schema()).MustExecute()
var newPostId int
m.Insert(
m.Permit("Title", "Picture").Filter(`{ "Title": "hello", "Picture": "world!" }`),
"CategoryId", 2,
).Returning("id").MustQueryRow(&newPostId)
// or:
m.Insert(
"Title", "hello",
"Picture", "world!",
"CategoryId", 2,
).Returning("id").MustQueryRow(&newPostId)
var firstPost Post
m.Find().Where("id = $1", newPostId).MustQuery(&firstPost)
// or: m.WHERE("id", "=", newPostId).Find().MustQuery(&firstPost)
// or: m.Where("id = $1", newPostId).Find().MustQuery(&firstPost)
// {1 2 hello world!}
var ids []int
m.Select("id").OrderBy("id ASC").MustQuery(&ids)
// [1]
// group results by key
var id2title map[int]string
m.Select("id", "title").MustQuery(&id2title)
// map[1:hello]
// map's key and value can be int, string, bool, array or struct
// if it is one-to-many, use slice as map's value
var postsByCategoryId map[struct{ categoryId int }][]struct{ title string }
m.Select("category_id", "title").MustQuery(&postsByCategoryId)
fmt.Println("map:", postsByCategoryId)
// map[{2}:[{hello}]]
var posts []Post
m.Find().MustQuery(&posts)
// [{1 2 hello world!}]
var rowsAffected int
m.Update(
m.Permit("Picture").Filter(`{ "Picture": "WORLD!" }`),
).Where("id = $1", newPostId).MustExecute(&rowsAffected)
// or: m.Where(...).Update(...).MustExecute(...)
var rowsDeleted int
m.Delete().Where("id = $1", newPostId).MustExecute(&rowsDeleted)
// or: m.Where(...).Delete().MustExecute(...)
m.Where("id = $1", newPostId).MustExists() // true or false
m.MustCount() // integer