Documentation
¶
Overview ¶
Package sqluct provides integration of sqlx and squirrel with Go structures.
Index ¶
- Constants
- func Columns(columns ...string) func(o *Options)
- func Get[V any](ctx context.Context, s *Storage, qb ToSQL) (V, error)
- func IgnoreOmitEmpty(o *Options)
- func InsertIgnore(o *Options)
- func List[V any](ctx context.Context, s *Storage, qb ToSQL) ([]V, error)
- func NoTableAll(ptrs ...interface{}) []interface{}
- func OrderDesc(o *Options)
- func QuoteANSI(tableAndColumn ...string) string
- func QuoteBackticks(tableAndColumn ...string) string
- func QuoteNoop(tableAndColumn ...string) string
- func QuoteRequiredANSI(tableAndColumn ...string) string
- func QuoteRequiredBackticks(tableAndColumn ...string) string
- func SkipZeroValues(o *Options)
- func SplitStatements(s string) []string
- func TxFromContext(ctx context.Context) *sqlx.Tx
- func TxToContext(ctx context.Context, tx *sqlx.Tx) context.Context
- type Dialect
- type JSON
- 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) 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 Plain
- type Quoted
- type QuotedNoTable
- type Referencer
- func (r *Referencer) AddTableAlias(rowStructPtr interface{}, alias string)
- func (r *Referencer) Col(ptr interface{}) string
- func (r *Referencer) Cols(ptr interface{}) []string
- func (r *Referencer) ColumnsOf(rowStructPtr interface{}) func(o *Options)
- func (r *Referencer) Eq(ptr interface{}, val interface{}) squirrel.Eq
- func (r *Referencer) Fmt(format string, ptrs ...interface{}) string
- func (r *Referencer) Q(tableAndColumn ...string) Quoted
- func (r *Referencer) Ref(ptr interface{}) string
- func (r *Referencer) Refs(ptrs ...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) MakeReferencer() *Referencer
- func (s *Storage) Query(ctx context.Context, qb ToSQL) (*sqlx.Rows, error)
- func (s *Storage) QueryBuilder() squirrel.StatementBuilderType
- 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 StorageOf
- func (s *StorageOf[V]) DeleteStmt() squirrel.DeleteBuilder
- func (s *StorageOf[V]) Get(ctx context.Context, qb ToSQL) (V, error)
- func (s *StorageOf[V]) InsertRow(ctx context.Context, row V, options ...func(o *Options)) (int64, error)
- func (s *StorageOf[V]) InsertRows(ctx context.Context, rows []V, options ...func(o *Options)) (sql.Result, error)
- func (s *StorageOf[V]) List(ctx context.Context, qb ToSQL) ([]V, error)
- func (s *StorageOf[V]) SelectStmt(options ...func(*Options)) squirrel.SelectBuilder
- func (s *StorageOf[V]) UpdateStmt(value any, options ...func(*Options)) squirrel.UpdateBuilder
- type StringStatement
- type ToSQL
Examples ¶
Constants ¶
const ( DialectUnknown = Dialect("") DialectMySQL = Dialect("mysql") DialectPostgres = Dialect("postgres") DialectSQLite3 = Dialect("sqlite3") )
Supported dialects.
const SerialID = "serialIdentity"
SerialID is the name of field tag to indicate integer serial (auto increment) ID of the table.
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 InsertIgnore ¶ added in v0.1.10
func InsertIgnore(o *Options)
InsertIgnore enables ignoring of row conflict during INSERT.
func NoTableAll ¶ added in v0.2.4
func NoTableAll(ptrs ...interface{}) []interface{}
NoTableAll enables references without table prefix for all field pointers. It can be useful to prepare multiple variadic arguments.
r.Fmt("ON CONFLICT(%s) DO UPDATE SET %s = excluded.%s, %s = excluded.%s",
sqluct.NoTableAll(&row.ID, &row.F1, &row.F1, &row.F2, &row.F3)...)
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 QuoteRequiredANSI ¶ added in v0.2.6
QuoteRequiredANSI quotes symbol names that need quoting with double quotes.
Suitable for PostgreSQL, MySQL in ANSI SQL_MODE, SQLite statements.
func QuoteRequiredBackticks ¶ added in v0.2.5
QuoteRequiredBackticks quotes symbol names that need quoting with backticks.
Suitable for MySQL, SQLite statements. See also https://dev.mysql.com/doc/refman/8.4/en/identifiers.html.
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 SplitStatements ¶ added in v0.2.8
SplitStatements splits a string in multiple SQL statements separated by semicolon (';').
Semicolons in comments and string literals are not treated as separators.
func TxFromContext ¶
TxFromContext gets transaction or nil from context.
Types ¶
type JSON ¶ added in v0.2.1
type JSON[V any] struct { Val V }
JSON is a generic container to a serialized db column.
func (JSON[V]) MarshalJSON ¶ added in v0.2.2
MarshalJSON encodes container value as JSON.
func (*JSON[V]) UnmarshalJSON ¶ added in v0.2.2
UnmarshalJSON decodes JSON into container.
type Mapper ¶
type Mapper struct {
ReflectMapper *reflectx.Mapper
Dialect Dialect
// contains filtered or unexported fields
}
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
// InsertIgnore enables ignoring of row conflict during INSERT.
// Uses
// - INSERT IGNORE for MySQL,
// - INSERT OR IGNORE for SQLite3,
// - INSERT ... ON CONFLICT DO NOTHING for Postgres.
InsertIgnore bool
}
Options defines mapping and query building parameters.
type Quoted ¶ added in v0.2.0
type Quoted string
Quoted is a string that can be interpolated into an SQL statement as is.
type QuotedNoTable ¶ added in v0.2.4
type QuotedNoTable struct {
// contains filtered or unexported fields
}
QuotedNoTable is a container of field pointer that should be referenced without table.
func NoTable ¶ added in v0.2.4
func NoTable(ptr interface{}) QuotedNoTable
NoTable enables references without table prefix. So that `my_table`.`my_column` would be rendered as `my_column`.
r.Ref(sqluct.NoTable(&row.MyColumn))
r.Fmt("%s = 1", sqluct.NoTable(&row.MyColumn))
Such references may be useful for INSERT/UPDATE column expressions.
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) Col ¶ added in v0.2.0
func (r *Referencer) Col(ptr interface{}) string
Col returns unescaped column name for field pointer that was previously added with AddTableAlias.
It panics if pointer is unknown. Might be used with Options.Columns.
func (*Referencer) Cols ¶ added in v0.1.9
func (r *Referencer) Cols(ptr interface{}) []string
Cols returns column references of a row structure.
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) Eq ¶ added in v0.2.0
func (r *Referencer) Eq(ptr interface{}, val interface{}) squirrel.Eq
Eq is a shortcut for squirrel.Eq{r.Ref(ptr): val}.
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.
It panics if pointer is unknown or is not a Quoted string.
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) Quoted
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.
func (*Referencer) Refs ¶ added in v0.2.4
func (r *Referencer) Refs(ptrs ...interface{}) []string
Refs returns reference strings for multiple field pointers.
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 Open ¶ added in v0.1.11
Open opens a database specified by its database driver name and a driver-specific data source name, usually consisting of at least a database name and connection information.
Example ¶
package main
import (
"context"
"log"
"github.com/bool64/sqluct"
)
func main() {
// Open DB connection.
st, err := sqluct.Open(
"postgres",
"postgres://pqgotest:password@localhost/pqgotest?sslmode=disable",
)
if err != nil {
log.Fatal(err.Error())
}
// Use Storage.
var foo []struct {
Bar string `db:"bar"`
}
err = st.Select(context.TODO(), sqluct.StringStatement("SELECT bar FROM foo"), &foo)
if err != nil {
log.Fatal(err.Error())
}
}
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(_ 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) MakeReferencer ¶ added in v0.2.0
func (s *Storage) MakeReferencer() *Referencer
MakeReferencer creates Referencer for query builder.
func (*Storage) Query ¶
Query queries database and returns raw result.
You must close the rows after use to avoid resource leak. 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) 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.MakeReferencer()
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"
)
func main() {
var (
s sqluct.Storage
ctx context.Context
)
// 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 StorageOf ¶ added in v0.2.0
type StorageOf[V any] struct { *Referencer R *V // contains filtered or unexported fields }
StorageOf is a type-safe facade to work with rows of specific type.
func Table ¶ added in v0.2.0
Table configures and returns StorageOf in a table.
Example ¶
var (
st = sqluct.NewStorage(sqlx.NewDb(sql.OpenDB(dumpConnector{}), "postgres"))
ctx = context.Background()
)
st.IdentifierQuoter = sqluct.QuoteANSI
type User struct {
ID int `db:"id"`
RoleID int `db:"role_id"`
Name string `db:"name"`
}
// Users repository.
ur := sqluct.Table[User](st, "users")
// Pointer to row, that can be used to reference columns via struct fields.
_ = ur.R
// Single user record can be inserted, last insert id (if available) and error are returned.
fmt.Println("Insert single user.")
_, _ = ur.InsertRow(ctx, User{Name: "John Doe", ID: 123})
// Multiple user records can be inserted with sql.Result and error returned.
fmt.Println("Insert two users.")
_, _ = ur.InsertRows(ctx, []User{{Name: "Jane Doe", ID: 124}, {Name: "Richard Roe", ID: 125}})
// Update statement for a single user with condition.
fmt.Println("Update a user with new name.")
_, _ = ur.UpdateStmt(User{Name: "John Doe, Jr.", ID: 123}).Where(ur.Eq(&ur.R.ID, 123)).ExecContext(ctx)
// Delete statement for a condition.
fmt.Println("Delete a user with id 123.")
_, _ = ur.DeleteStmt().Where(ur.Eq(&ur.R.ID, 123)).ExecContext(ctx)
fmt.Println("Get single user with id = 123.")
user, _ := ur.Get(ctx, ur.SelectStmt().Where(ur.Eq(&ur.R.ID, 123)))
// Squirrel expression can be formatted with %s reference(s) to column pointer.
fmt.Println("Get multiple users with names starting with 'John '.")
users, _ := ur.List(ctx, ur.SelectStmt().Where(ur.Fmt("%s LIKE ?", &ur.R.Name), "John %"))
// Squirrel expressions can be applied.
fmt.Println("Get multiple users with id != 123.")
users, _ = ur.List(ctx, ur.SelectStmt().Where(squirrel.NotEq(ur.Eq(&ur.R.ID, 123))))
fmt.Println("Get all users.")
users, _ = ur.List(ctx, ur.SelectStmt())
// More complex statements can be made with references to other tables.
type Role struct {
ID int `db:"id"`
Name string `db:"name"`
}
// Roles repository.
rr := sqluct.Table[Role](st, "roles")
// To be able to resolve "roles" columns, we need to attach roles repo to users repo.
ur.AddTableAlias(rr.R, "roles")
fmt.Println("Get users with role 'admin'.")
users, _ = ur.List(ctx, ur.SelectStmt().
LeftJoin(ur.Fmt("%s ON %s = %s", rr.R, &rr.R.ID, &ur.R.RoleID)).
Where(ur.Fmt("%s = ?", &rr.R.Name), "admin"),
)
_ = user
_ = users
Output: Insert single user. exec INSERT INTO "users" ("id","role_id","name") VALUES ($1,$2,$3) [123 0 John Doe] Insert two users. exec INSERT INTO "users" ("id","role_id","name") VALUES ($1,$2,$3),($4,$5,$6) [124 0 Jane Doe 125 0 Richard Roe] Update a user with new name. exec UPDATE "users" SET "id" = $1, "role_id" = $2, "name" = $3 WHERE "users"."id" = $4 [123 0 John Doe, Jr. 123] Delete a user with id 123. exec DELETE FROM "users" WHERE "users"."id" = $1 [123] Get single user with id = 123. query SELECT "users"."id", "users"."role_id", "users"."name" FROM "users" WHERE "users"."id" = $1 [123] Get multiple users with names starting with 'John '. query SELECT "users"."id", "users"."role_id", "users"."name" FROM "users" WHERE "users"."name" LIKE $1 [John %] Get multiple users with id != 123. query SELECT "users"."id", "users"."role_id", "users"."name" FROM "users" WHERE "users"."id" <> $1 [123] Get all users. query SELECT "users"."id", "users"."role_id", "users"."name" FROM "users" [] Get users with role 'admin'. query SELECT "users"."id", "users"."role_id", "users"."name" FROM "users" LEFT JOIN "roles" ON "roles"."id" = "users"."role_id" WHERE "roles"."name" = $1 [admin]
func (*StorageOf[V]) DeleteStmt ¶ added in v0.2.0
func (s *StorageOf[V]) DeleteStmt() squirrel.DeleteBuilder
DeleteStmt creates delete statement with table name.
func (*StorageOf[V]) InsertRow ¶ added in v0.2.0
func (s *StorageOf[V]) InsertRow(ctx context.Context, row V, options ...func(o *Options)) (int64, error)
InsertRow inserts single row database table.
func (*StorageOf[V]) InsertRows ¶ added in v0.2.0
func (s *StorageOf[V]) InsertRows(ctx context.Context, rows []V, options ...func(o *Options)) (sql.Result, error)
InsertRows inserts multiple rows in database table.
func (*StorageOf[V]) List ¶ added in v0.2.0
List retrieves a collection of rows from database storage.
func (*StorageOf[V]) SelectStmt ¶ added in v0.2.0
func (s *StorageOf[V]) SelectStmt(options ...func(*Options)) squirrel.SelectBuilder
SelectStmt creates query statement with table name and row columns.
func (*StorageOf[V]) UpdateStmt ¶ added in v0.2.0
func (s *StorageOf[V]) UpdateStmt(value any, options ...func(*Options)) squirrel.UpdateBuilder
UpdateStmt creates update statement with table name and updated value (can be nil).
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.