Documentation
¶
Overview ¶
Package sqluct provides integration of sqlx and squirrel with Go structures.
Index ¶
- func Columns(columns ...string) func(o *Options)
- func IgnoreOmitEmpty(o *Options)
- func OrderDesc(o *Options)
- func QuoteANSI(tableAndColumn ...string) string
- func QuoteBackticks(tableAndColumn ...string) string
- func QuoteNoop(tableAndColumn ...string) string
- func SkipZeroValues(o *Options)
- func TxFromContext(ctx context.Context) *sqlx.Tx
- func TxToContext(ctx context.Context, tx *sqlx.Tx) context.Context
- type Mapper
- func (sm *Mapper) Col(structPtr, fieldPtr interface{}) string
- func (sm *Mapper) ColumnsValues(v reflect.Value, options ...func(*Options)) ([]string, []interface{})
- func (sm *Mapper) FindColumnName(structPtr, fieldPtr interface{}) (string, error)
- func (sm *Mapper) FindColumnNames(structPtr interface{}) (map[interface{}]string, error)
- func (sm *Mapper) Insert(q squirrel.InsertBuilder, val interface{}, options ...func(*Options)) squirrel.InsertBuilder
- func (sm *Mapper) Order(columns interface{}, options ...func(*Options)) stringdeprecated
- func (sm *Mapper) Select(q squirrel.SelectBuilder, columns interface{}, options ...func(*Options)) squirrel.SelectBuilder
- func (sm *Mapper) Update(q squirrel.UpdateBuilder, val interface{}, options ...func(*Options)) squirrel.UpdateBuilder
- func (sm *Mapper) WhereEq(conditions interface{}, options ...func(*Options)) squirrel.Eq
- type Options
- type Referencer
- func (r *Referencer) AddTableAlias(rowStructPtr interface{}, alias string)
- func (r *Referencer) ColumnsOf(rowStructPtr interface{}) func(o *Options)
- func (r *Referencer) Fmt(format string, ptrs ...interface{}) string
- func (r *Referencer) Q(tableAndColumn ...string) string
- func (r *Referencer) Ref(ptr interface{}) string
- type Storage
- func (s *Storage) Col(structPtr, fieldPtr interface{}) string
- func (s *Storage) DB() *sqlx.DB
- func (s *Storage) DeleteStmt(tableName string) squirrel.DeleteBuilder
- func (s *Storage) Exec(ctx context.Context, qb ToSQL) (res sql.Result, err error)
- func (s *Storage) InTx(ctx context.Context, fn func(context.Context) error) (err error)
- func (s *Storage) InsertStmt(tableName string, val interface{}, options ...func(*Options)) squirrel.InsertBuilder
- func (s *Storage) Query(ctx context.Context, qb ToSQL) (*sqlx.Rows, error)
- func (s *Storage) QueryBuilder() squirrel.StatementBuilderType
- func (s *Storage) Ref() *Referencer
- func (s *Storage) Select(ctx context.Context, qb ToSQL, dest interface{}) (err error)
- func (s *Storage) SelectStmt(tableName string, columns interface{}, options ...func(*Options)) squirrel.SelectBuilder
- func (s *Storage) UpdateStmt(tableName string, val interface{}, options ...func(*Options)) squirrel.UpdateBuilder
- func (s *Storage) WhereEq(conditions interface{}, options ...func(*Options)) squirrel.Eq
- type StringStatement
- type ToSQL
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func IgnoreOmitEmpty ¶ added in v0.1.5
func IgnoreOmitEmpty(o *Options)
IgnoreOmitEmpty instructs mapper to use zero values of fields with `omitempty`.
func OrderDesc ¶
func OrderDesc(o *Options)
OrderDesc instructs mapper to use DESC order in Product func.
func QuoteANSI ¶ added in v0.1.3
QuoteANSI adds double quotes to symbols names.
Suitable for PostgreSQL, MySQL in ANSI SQL_MODE, SQLite statements.
func QuoteBackticks ¶ added in v0.1.3
QuoteBackticks quotes symbol names with backticks.
Suitable for MySQL, SQLite statements.
func QuoteNoop ¶ added in v0.1.3
QuoteNoop does not add any quotes to symbol names.
Used in Referencer by default.
func SkipZeroValues ¶
func SkipZeroValues(o *Options)
SkipZeroValues instructs mapper to ignore fields with zero values.
Example ¶
package main
import (
"fmt"
"github.com/bool64/sqluct"
)
func main() {
var s sqluct.Storage
type Product struct {
ID int `db:"id,omitempty"`
Name string `db:"name,omitempty"`
Price int `db:"price"`
}
query, args, err := s.SelectStmt("products", Product{}).Where(s.WhereEq(Product{
ID: 123,
Price: 0,
})).ToSql()
fmt.Println(query, args, err)
// This query skips `name` in where condition for its zero value and `omitempty` flag.
// SELECT id, name, price FROM products WHERE id = $1 AND price = $2 [123 0] <nil>
query, args, err = s.SelectStmt("products", Product{}).Where(s.WhereEq(Product{
ID: 123,
Price: 0,
}, sqluct.IgnoreOmitEmpty)).ToSql()
fmt.Println(query, args, err)
// This query adds `name` in where condition because IgnoreOmitEmpty is applied and `omitempty` flag is ignored.
// SELECT id, name, price FROM products WHERE id = $1 AND name = $2 AND price = $3 [123 0] <nil>
query, args, err = s.SelectStmt("products", Product{}).Where(s.WhereEq(Product{
ID: 123,
Price: 0,
}, sqluct.SkipZeroValues)).ToSql()
fmt.Println(query, args, err)
// This query adds skips both price and name from where condition because SkipZeroValues option is applied.
// SELECT id, name, price FROM products WHERE id = $1 [123] <nil>
}
Output: SELECT id, name, price FROM products WHERE id = $1 AND price = $2 [123 0] <nil> SELECT id, name, price FROM products WHERE id = $1 AND name = $2 AND price = $3 [123 0] <nil> SELECT id, name, price FROM products WHERE id = $1 [123] <nil>
func TxFromContext ¶
TxFromContext gets transaction or nil from context.
Types ¶
type Mapper ¶
Mapper prepares select, insert and update statements.
func (*Mapper) Col ¶
Col will try to find column name and will panic on error.
Example ¶
package main
import (
"fmt"
"time"
"github.com/Masterminds/squirrel"
"github.com/bool64/sqluct"
)
func main() {
sm := sqluct.Mapper{}
type Order struct {
ID int `db:"order_id,omitempty"`
CreatedAt time.Time `db:"created_at,omitempty"`
}
o := Order{
ID: 123,
}
q := sm.
Select(squirrel.Select(), o).
From("orders").
Where(squirrel.Eq{
sm.Col(&o, &o.ID): o.ID, // Col returns "order_id" defined in field tag.
})
query, args, err := q.ToSql()
fmt.Println(query, args, err)
}
Output: SELECT order_id, created_at FROM orders WHERE order_id = ? [123] <nil>
func (*Mapper) ColumnsValues ¶ added in v0.1.1
func (sm *Mapper) ColumnsValues(v reflect.Value, options ...func(*Options)) ([]string, []interface{})
ColumnsValues extracts columns and values from provided struct value.
func (*Mapper) FindColumnName ¶
FindColumnName returns column name of a database entity field.
Entity field is defined by pointer to owner structure and pointer to field in that structure.
entity := MyEntity{}
name, found := sm.FindColumnName(&entity, &entity.UpdatedAt)
func (*Mapper) FindColumnNames ¶ added in v0.1.3
FindColumnNames returns column names mapped by a pointer to a field.
func (*Mapper) Insert ¶
func (sm *Mapper) Insert(q squirrel.InsertBuilder, val interface{}, options ...func(*Options)) squirrel.InsertBuilder
Insert adds struct value or slice of struct values to squirrel.InsertBuilder.
Example ¶
package main
import (
"fmt"
"github.com/Masterminds/squirrel"
"github.com/bool64/sqluct"
)
func main() {
sm := sqluct.Mapper{}
type Order struct {
ID int `db:"order_id,omitempty"`
Amount int `db:"amount"`
UserID int `db:"user_id"`
}
o := Order{}
o.Amount = 100
o.UserID = 123
q := sm.Insert(squirrel.Insert("orders"), o)
query, args, err := q.ToSql()
fmt.Println(query, args, err)
}
Output: INSERT INTO orders (amount,user_id) VALUES (?,?) [100 123] <nil>
func (*Mapper) Select ¶
func (sm *Mapper) Select(q squirrel.SelectBuilder, columns interface{}, options ...func(*Options)) squirrel.SelectBuilder
Select maps struct field tags as columns to squirrel.SelectBuilder, slice of struct is also accepted.
Example ¶
package main
import (
"fmt"
"github.com/Masterminds/squirrel"
"github.com/bool64/sqluct"
)
func main() {
sm := sqluct.Mapper{}
type OrderData struct {
Amount int `db:"amount"`
UserID int `db:"user_id,omitempty"`
}
type Order struct {
ID int `db:"order_id,omitempty"`
OrderData
}
o := Order{}
o.ID = 321
q := sm.
Select(squirrel.Select(), o).
Where(squirrel.Eq{sm.Col(&o, &o.ID): o.ID})
query, args, err := q.ToSql()
fmt.Println(query, args, err)
}
Output: SELECT order_id, amount, user_id WHERE order_id = ? [321] <nil>
func (*Mapper) Update ¶
func (sm *Mapper) Update(q squirrel.UpdateBuilder, val interface{}, options ...func(*Options)) squirrel.UpdateBuilder
Update sets struct value to squirrel.UpdateBuilder.
Example ¶
package main
import (
"fmt"
"github.com/Masterminds/squirrel"
"github.com/bool64/sqluct"
)
func main() {
sm := sqluct.Mapper{}
type OrderData struct {
Amount int `db:"amount"`
UserID int `db:"user_id,omitempty"`
}
type Order struct {
ID int `db:"order_id,omitempty"`
OrderData
}
o := Order{}
o.ID = 321
o.Amount = 100
o.UserID = 123
q := sm.
Update(squirrel.Update("orders"), o.OrderData).
Where(squirrel.Eq{sm.Col(&o, &o.ID): o.ID})
query, args, err := q.ToSql()
fmt.Println(query, args, err)
}
Output: UPDATE orders SET amount = ?, user_id = ? WHERE order_id = ? [100 123 321] <nil>
func (*Mapper) WhereEq ¶
WhereEq maps struct values as conditions to squirrel.Eq.
Example ¶
package main
import (
"fmt"
"github.com/Masterminds/squirrel"
"github.com/bool64/sqluct"
)
func main() {
sm := sqluct.Mapper{}
type OrderData struct {
Amount int `db:"amount"`
UserID int `db:"user_id,omitempty"`
}
type Order struct {
ID int `db:"order_id"`
OrderData
}
o := Order{}
o.Amount = 100
o.UserID = 123
q := sm.
Select(squirrel.Select().From("orders"), o).
Where(sm.WhereEq(o.OrderData))
query, args, err := q.ToSql()
fmt.Println(query, args, err)
}
Output: SELECT order_id, amount, user_id FROM orders WHERE amount = ? AND user_id = ? [100 123] <nil>
Example (ColumnsOf) ¶
package main
import (
"fmt"
"github.com/Masterminds/squirrel"
"github.com/bool64/sqluct"
)
func main() {
sm := sqluct.Mapper{}
type OrderData struct {
Amount int `db:"amount"`
UserID int `db:"user_id,omitempty"`
}
type Order struct {
ID int `db:"id"`
OrderData
}
type User struct {
ID int `db:"id"`
Name string `db:"name"`
}
rf := sqluct.Referencer{}
o := &Order{}
u := &User{}
rf.AddTableAlias(o, "orders")
rf.AddTableAlias(u, "users")
q := sm.
Select(squirrel.Select().From(rf.Ref(o)), o, rf.ColumnsOf(o)).
Join(rf.Fmt("%s ON %s = %s", u, &o.UserID, &u.ID)).
Where(sm.WhereEq(OrderData{
Amount: 100,
UserID: 123,
}, rf.ColumnsOf(o)))
query, args, err := q.ToSql()
fmt.Println(query, args, err)
}
Output: SELECT orders.id, orders.amount, orders.user_id FROM orders JOIN users ON orders.user_id = users.id WHERE orders.amount = ? AND orders.user_id = ? [100 123] <nil>
type Options ¶
type Options struct {
// SkipZeroValues instructs mapper to ignore fields with zero values regardless of `omitempty` tag.
SkipZeroValues bool
// IgnoreOmitEmpty instructs mapper to use zero values of fields with `omitempty`.
IgnoreOmitEmpty bool
// Columns is used to control which columns from the structure should be used.
Columns []string
// OrderDesc instructs mapper to use DESC order in Product func.
OrderDesc bool
// PrepareColumn allows control of column quotation or aliasing.
PrepareColumn func(col string) string
}
Options defines mapping parameters.
type Referencer ¶ added in v0.1.3
type Referencer struct {
Mapper *Mapper
// IdentifierQuoter is formatter of column and table names.
// Default QuoteNoop.
IdentifierQuoter func(tableAndColumn ...string) string
// contains filtered or unexported fields
}
Referencer maintains a list of string references to fields and table aliases.
func (*Referencer) AddTableAlias ¶ added in v0.1.3
func (r *Referencer) AddTableAlias(rowStructPtr interface{}, alias string)
AddTableAlias creates string references for row pointer and all suitable field pointers in it.
Empty alias is not added to column reference.
func (*Referencer) ColumnsOf ¶ added in v0.1.6
func (r *Referencer) ColumnsOf(rowStructPtr interface{}) func(o *Options)
ColumnsOf makes a Mapper option to prefix columns with table alias.
Argument is either a structure pointer or string alias.
func (*Referencer) Fmt ¶ added in v0.1.3
func (r *Referencer) Fmt(format string, ptrs ...interface{}) string
Fmt formats according to a format specified replacing ptrs with their reference strings where possible.
Values that are not available as reference string are passed to fmt.Sprintf as is.
Example ¶
package main
import (
"fmt"
"log"
"github.com/Masterminds/squirrel"
"github.com/bool64/sqluct"
)
func main() {
type User struct {
ID int `db:"id"`
FirstName string `db:"first_name"`
LastName string `db:"last_name"`
}
type DirectReport struct {
ManagerID int `db:"manager_id"`
EmployeeID int `db:"employee_id"`
}
rf := sqluct.Referencer{}
manager := &User{}
rf.AddTableAlias(manager, "manager")
employee := &User{}
rf.AddTableAlias(employee, "employee")
dr := &DirectReport{}
rf.AddTableAlias(dr, "dr")
// Find direct reports that share same last name and manager is not named John.
qb := squirrel.StatementBuilder.Select(rf.Fmt("%s, %s", &dr.ManagerID, &dr.EmployeeID)).
From(rf.Fmt("%s AS %s", rf.Q("users"), manager)).
InnerJoin(rf.Fmt("%s AS %s ON %s = %s AND %s = %s",
rf.Q("direct_reports"), dr,
&dr.ManagerID, &manager.ID,
&dr.EmployeeID, &employee.ID)).
Where(rf.Fmt("%s = %s", &manager.LastName, &employee.LastName)).
Where(rf.Fmt("%s != ?", &manager.FirstName), "John")
stmt, args, err := qb.ToSql()
if err != nil {
log.Fatal(err)
}
fmt.Println(stmt)
fmt.Println(args)
}
Output: SELECT dr.manager_id, dr.employee_id FROM users AS manager INNER JOIN direct_reports AS dr ON dr.manager_id = manager.id AND dr.employee_id = employee.id WHERE manager.last_name = employee.last_name AND manager.first_name != ? [John]
func (*Referencer) Q ¶ added in v0.1.3
func (r *Referencer) Q(tableAndColumn ...string) string
Q quotes identifier.
func (*Referencer) Ref ¶ added in v0.1.3
func (r *Referencer) Ref(ptr interface{}) string
Ref returns reference string for struct or field pointer that was previously added with AddTableAlias.
It panics if pointer is unknown.
type Storage ¶
type Storage struct {
Mapper *Mapper
// Format is a placeholder format, default squirrel.Dollar.
// Other values are squirrel.Question, squirrel.AtP and squirrel.Colon.
Format squirrel.PlaceholderFormat
// IdentifierQuoter is formatter of column and table names.
// Default QuoteNoop.
IdentifierQuoter func(tableAndColumn ...string) string
// OnError is called when error is encountered, could be useful for logging.
OnError func(ctx context.Context, err error)
// Trace wraps a call to database.
// It takes statement as arguments and returns
// instrumented context with callback to call after db call is finished.
Trace func(ctx context.Context, stmt string, args []interface{}) (newCtx context.Context, onFinish func(error))
// contains filtered or unexported fields
}
Storage creates and executes database statements.
func (*Storage) DeleteStmt ¶
func (s *Storage) DeleteStmt(tableName string) squirrel.DeleteBuilder
DeleteStmt makes a delete query builder.
func (*Storage) InTx ¶
InTx runs callback in a transaction.
If transaction already exists, it will reuse that. Otherwise it starts a new transaction and commit or rollback (in case of error) at the end.
Example ¶
package main
import (
"context"
"log"
"github.com/bool64/sqluct"
)
func main() {
var (
s sqluct.Storage
ctx context.Context
)
err := s.InTx(ctx, func(ctx context.Context) error {
return nil
})
if err != nil {
log.Fatal(err)
}
}
Example (Full) ¶
package main
import (
"context"
"log"
"time"
"github.com/Masterminds/squirrel"
"github.com/bool64/sqluct"
)
func main() {
var (
s sqluct.Storage
ctx context.Context
)
const tableName = "products"
type Product struct {
ID int `db:"id"`
Title string `db:"title"`
CreatedAt time.Time `db:"created_at"`
}
// INSERT INTO products (id, title, created_at) VALUES (1, 'Apples', <now>), (2, 'Oranges', <now>)
_, err := s.Exec(ctx, s.InsertStmt(tableName, []Product{
{
ID: 1,
Title: "Apples",
CreatedAt: time.Now(),
}, {
ID: 2,
Title: "Oranges",
CreatedAt: time.Now(),
},
}))
if err != nil {
log.Fatal(err)
}
// UPDATE products SET title = 'Bananas' WHERE id = 2
_, err = s.Exec(
ctx,
s.UpdateStmt(tableName, Product{Title: "Bananas"}, sqluct.SkipZeroValues).
Where(s.WhereEq(Product{ID: 2}, sqluct.SkipZeroValues)),
)
if err != nil {
log.Fatal(err)
}
var (
result []Product
row Product
)
// SELECT id, title, created_at FROM products WHERE id != 3 AND created_at <= <now>
err = s.Select(ctx,
s.SelectStmt(tableName, row).
Where(squirrel.NotEq(s.WhereEq(Product{ID: 3}, sqluct.SkipZeroValues))).
Where(squirrel.LtOrEq{s.Col(&row, &row.CreatedAt): time.Now()}),
&result,
)
if err != nil {
log.Fatal(err)
}
// DELETE FROM products WHERE id = 2
_, err = s.Exec(ctx, s.DeleteStmt(tableName).Where(Product{ID: 2}, sqluct.SkipZeroValues))
if err != nil {
log.Fatal(err)
}
}
func (*Storage) InsertStmt ¶
func (s *Storage) InsertStmt(tableName string, val interface{}, options ...func(*Options)) squirrel.InsertBuilder
InsertStmt makes an insert query builder.
Example ¶
package main
import (
"context"
"log"
"github.com/bool64/sqluct"
)
func main() {
var (
s sqluct.Storage
ctx context.Context
)
type MyEntity struct {
Name string `db:"name"`
Age int `db:"age"`
}
row := MyEntity{
Name: "Jane",
Age: 30,
}
qb := s.InsertStmt("my_table", row)
if _, err := s.Exec(ctx, qb); err != nil {
log.Fatal(err)
}
}
func (*Storage) Query ¶
Query queries database and returns raw result.
Select is recommended to use instead of Query.
func (*Storage) QueryBuilder ¶
func (s *Storage) QueryBuilder() squirrel.StatementBuilderType
QueryBuilder returns query builder with placeholder format.
func (*Storage) Ref ¶ added in v0.1.3
func (s *Storage) Ref() *Referencer
Ref creates Referencer for query builder.
func (*Storage) Select ¶
Select queries statement of query builder and scans result into destination.
Destination can be a pointer to struct or slice, e.g. `*row` or `*[]row`.
Example (Join) ¶
package main
import (
"fmt"
"github.com/bool64/sqluct"
)
func main() {
var s sqluct.Storage
type OrderData struct {
Amount int `db:"amount"`
UserID int `db:"user_id,omitempty"`
}
type Order struct {
ID int `db:"id"`
OrderData
}
type User struct {
ID int `db:"id"`
Name string `db:"name"`
}
rf := s.Ref()
o := &Order{}
u := &User{}
rf.AddTableAlias(o, "orders")
rf.AddTableAlias(u, "users")
q := s.SelectStmt(rf.Ref(o), o, rf.ColumnsOf(o)).
Columns(rf.Ref(&u.Name)).
Join(rf.Fmt("%s ON %s = %s", u, &o.UserID, &u.ID)).
Where(s.WhereEq(OrderData{
Amount: 100,
UserID: 123,
}, rf.ColumnsOf(o)))
query, args, err := q.ToSql()
fmt.Println(query, args, err)
}
Output: SELECT orders.id, orders.amount, orders.user_id, users.name FROM orders JOIN users ON orders.user_id = users.id WHERE orders.amount = $1 AND orders.user_id = $2 [100 123] <nil>
Example (OneRow) ¶
package main
import (
"context"
"log"
"github.com/bool64/sqluct"
)
func main() {
var (
s sqluct.Storage
ctx context.Context
)
type MyEntity struct {
Name string `db:"name"`
Age int `db:"age"`
}
var row MyEntity
qb := s.SelectStmt("my_table", row)
if err := s.Select(ctx, qb, &row); err != nil {
log.Fatal(err)
}
}
Example (Slice) ¶
package main
import (
"context"
"fmt"
"log"
"github.com/bool64/sqluct"
"github.com/jmoiron/sqlx"
)
func main() {
var (
db *sqlx.DB // Setup db connection.
ctx context.Context
)
s := sqluct.NewStorage(db)
// Define your entity as a struct with `db` field tags that correspond to column names in table.
type MyEntity struct {
Name string `db:"name"`
Age int `db:"age"`
}
// Create destination for query result.
rows := make([]MyEntity, 0, 100)
// Create SELECT statement from fields of entity.
qb := s.SelectStmt("my_table", MyEntity{}).
Where(s.WhereEq(MyEntity{
Name: "Jane",
}, sqluct.SkipZeroValues)) // Add WHERE condition built from fields of entity.
// Query statement would be
// SELECT name, age FROM my_table WHERE name = $1
// with argument 'Jane'.
err := s.Select(ctx, qb, &rows)
if err != nil {
log.Fatal(err)
}
for _, row := range rows {
fmt.Println(row)
}
}
func (*Storage) SelectStmt ¶
func (s *Storage) SelectStmt(tableName string, columns interface{}, options ...func(*Options)) squirrel.SelectBuilder
SelectStmt makes a select query builder.
func (*Storage) UpdateStmt ¶
func (s *Storage) UpdateStmt(tableName string, val interface{}, options ...func(*Options)) squirrel.UpdateBuilder
UpdateStmt makes an update query builder.
Example ¶
package main
import (
"context"
"log"
"github.com/bool64/sqluct"
)
func main() {
var (
s sqluct.Storage
ctx context.Context
)
type MyIdentity struct {
ID int `db:"id"`
}
type MyValue struct {
Name string `db:"name"`
Age int `db:"age"`
}
row := MyValue{
Name: "Jane",
Age: 30,
}
qb := s.UpdateStmt("my_table", row).
Where(s.WhereEq(MyIdentity{ID: 123}))
if _, err := s.Exec(ctx, qb); err != nil {
log.Fatal(err)
}
}
type StringStatement ¶
type StringStatement string
StringStatement is a plain string statement.
func (StringStatement) ToSql ¶
func (s StringStatement) ToSql() (string, []interface{}, error)
ToSql implements query builder result.