Documentation ¶
Overview ¶
Package bsq helps to build structured queries for SQL in a maintainable manner. Unlike many query builders out there it does not strive to formalize SQL as Go DSL. Its primary focus it code maintainability when working with SQL in your code. I.e. consider the problem of keeping the Go code in line with the DB model to be the primary maintenance task: It must easy to find all parts of the code that have to change when the DB model changes.
Now, consider some imaginary fluent SQL builder API that let's you do something like this:
sql := Select("name", "age").From("users").Where(EqArg("id"))
And this might yield the expected string:
SELECT name, age FROM users WHERE id=?
That's fine and the fluent API might have helped to spell SQL keywords correctly. But what if the users table renames a column or gets a new column? How to you find the Go code that relate to the users table? At best a clever tool can search for all uses of the From() method that gets the string "users" as an argument. Without such tool you have to try a full text search.
bsq takes a different approach. This approach starts with a minimal replica of the DB model made of Go objects:
var tUsers = struct { Table ID, Name, Age Column }{ Table: Table{TableName: "users"}, }
The extra effort for this replica helps much with the maintainability problem. Consider this conceptual example (that omits some syntactic sugar which actually would improve the code):
sql := WriteSQL("SELECT ", &tUsers.Name, ", ", &tUsers.Age, " FROM ", &tUsers, " WHERE ", &tUsers.ID, '=', P("id"))
While this does not help with SQL syntax, one now can simply use standard developer tools to locate all references to the tUsers variable. And renaming a column can be done at one single location where the Name column is declared in the replica.
Package bsq builds on this concept to simplify the creation of SQL statements. E.g. to get the example SELECT statement one would actually write with bsq:
sql := MustSQLString(DefaultDialect, // Std SQL with positional parameters Select{ Cols: ColsOf(&tUsers, &tUsers.ID), EqCols: Cols(&tUsers.ID), })
For the further details read the following
Example ¶
tUsers := struct { Table ID, Name, Age Column }{ Table: Table{Name: "users"}, } InitDefns("", nil, &tUsers) sql, _ := MustSQLString( DefaultDialect, // Std SQL with positional parameters 0, // No flags Select{ Columns: ColsOf(&tUsers, &tUsers.ID), EqColumns: Cols(&tUsers.ID), }) fmt.Println(sql)
Output: SELECT Name, Age FROM users WHERE ID=?
Index ¶
- func AddDialect(name string, d Dialect) error
- func ForEachDefn(do func(Tabler) (done bool)) bool
- func InitDefns(schema string, opts *InitOpts, tableDefns ...Tabler) error
- func MustAddDialect(name string, d Dialect)
- func MustInitDefns(schema string, opts *InitOpts, tableDefns ...Tabler)
- func MustSQLString(d Dialect, flags WriteFlag, stmt ...interface{}) (string, interface{})
- func QueryCreate(db sqlize.Querier, q *Query, args ...interface{}) (id int64, err error)
- func QueryCreateNamed(db sqlize.Querier, q *Query, args ...sql.NamedArg) (id int64, err error)
- func QuoteKeywords(keywords []string, defn string) (escaped string)
- func SQLString(d Dialect, flags WriteFlag, stmt ...interface{}) (string, interface{}, error)
- func UpsertOnConflict(tbl *Table, conflict, set []Columner) []interface{}
- func VisitDefn(tdefn Tabler, v DefnVisitor) error
- func WriteSQL(wr io.Writer, d Dialect, flags WriteFlag, stmt ...interface{}) (params interface{}, err error)
- type ArgMap
- type CRUD
- func (crud *CRUD) Create(db sqlize.Querier, args ...interface{}) (id int64, err error)
- func (crud *CRUD) CreateNamed(db sqlize.Querier, args ...sql.NamedArg) (id int64, err error)
- func (crud *CRUD) Delete(db sqlize.Querier, id interface{}) (sql.Result, error)
- func (crud *CRUD) Read(db sqlize.Querier, id interface{}) *sql.Row
- func (crud *CRUD) Update(db sqlize.Querier, id interface{}, args ...interface{}) (sql.Result, error)
- func (crud *CRUD) UpdateNamed(db sqlize.Querier, id interface{}, args ...sql.NamedArg) (sql.Result, error)
- type Column
- type Columner
- type Create
- type DefnVisitor
- type Delete
- type Dialect
- type IndexedParams
- type InitDefn
- type InitOpts
- type Insert
- type JoinEq
- type List
- type NamedParams
- type OuterJoin
- type P
- type PEq
- type ParamNamer
- type PositionalParams
- type Query
- func (qdefn *Query) Bind(params ...sql.NamedArg) []interface{}
- func (defn *Query) Defn() []interface{}
- func (qdefn *Query) Dialect() Dialect
- func (qdefn *Query) Exec(db sqlize.Querier, args ...interface{}) (sql.Result, error)
- func (qdefn *Query) ExecContext(ctx context.Context, db sqlize.Querier, args ...interface{}) (sql.Result, error)
- func (qdefn *Query) ExecNamed(db sqlize.Querier, args ...sql.NamedArg) (sql.Result, error)
- func (qdefn *Query) ExecNamedContext(ctx context.Context, db sqlize.Querier, args ...sql.NamedArg) (sql.Result, error)
- func (defn *Query) Flags() WriteFlag
- func (qdefn *Query) MustSQL() string
- func (qdefn *Query) Prepare(db sqlize.Querier) (*sql.Stmt, error)
- func (qdefn *Query) PrepareContext(ctx context.Context, db sqlize.Querier) (*sql.Stmt, error)
- func (qdefn *Query) Query(db sqlize.Querier, args ...interface{}) (*sql.Rows, error)
- func (qdefn *Query) QueryContext(ctx context.Context, db sqlize.Querier, args ...interface{}) (*sql.Rows, error)
- func (qdefn *Query) QueryNamed(db sqlize.Querier, args ...sql.NamedArg) (*sql.Rows, error)
- func (qdefn *Query) QueryNamedContext(ctx context.Context, db sqlize.Querier, args ...sql.NamedArg) (*sql.Rows, error)
- func (qdefn *Query) QueryRow(db sqlize.Querier, args ...interface{}) *sql.Row
- func (qdefn *Query) QueryRowContext(ctx context.Context, db sqlize.Querier, args ...interface{}) *sql.Row
- func (qdefn *Query) QueryRowNamed(db sqlize.Querier, args ...sql.NamedArg) *sql.Row
- func (qdefn *Query) QueryRowNamedContext(ctx context.Context, db sqlize.Querier, args ...sql.NamedArg) *sql.Row
- func (qdefn *Query) SQL() (string, error)
- func (qdefn Query) With(d Dialect, flags WriteFlag) Query
- func (qdefn Query) WithDialect(d Dialect) Query
- func (qdefn Query) WithFlags(flags WriteFlag) Query
- type SQLWriterTo
- type Select
- type Table
- type TableAlias
- type Tabler
- type Update
- type Upsert
- type WriteFlag
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func InitDefns ¶
Initialize Table definitions of the replica DB model. Without initialization the model cannot be used. See package bsq example.
func MustSQLString ¶
func QueryCreate ¶ added in v0.2.0
QueryCreate executes a Create query defined as q with argument values passed as args. Args must match the argument mapping of the used sql driver.
func QueryCreateNamed ¶ added in v0.2.0
CreateNamedArgs executes a Create query defined as q with arguments passed as sql.NamedArgs. Is uses the ArgMap computed by q's Dialect to map the arguments.
func SQLString ¶
func UpsertOnConflict ¶
Example ¶
defn := UpsertOnConflict( &testTable.Table, Cols(&testTable.Name), ColsOf(&testTable, &testTable.Name), ) stmt, _ := MustSQLString(DefaultDialect, 0, defn...) fmt.Println(stmt)
Output: INSERT INTO bsq.users (Name, ID, Age) VALUES (?, ?, ?) ON CONFLICT (Name) DO UPDATE SET ID=?, Age=?
func VisitDefn ¶
func VisitDefn(tdefn Tabler, v DefnVisitor) error
func WriteSQL ¶
func WriteSQL(wr io.Writer, d Dialect, flags WriteFlag, stmt ...interface{}) ( params interface{}, err error, )
WriteSQL writes all elements of an SQL stmt to wr using the SQL Dialect d. Elements may be runes, strings, query parameters P and everything that implements SQLWriterTo. WriteSQL is the core of bsq but in most caes it will not be used diretcly by applications. E.g. QueryDefn lets you define queries as global variables that lazily build the query protected by sync.Once.
Example ¶
var tbl = struct { Table Col1 Column }{ Table: Table{Name: "users", Schema: "bsq"}, } InitDefns("", nil, &tbl) var sb strings.Builder alias := tbl.Alias("u") WriteSQL(&sb, DefaultDialect, 0, "SELECT ", ColList(tbl.Col1.Q(""), alias.Q(&tbl.Col1)), " FROM ", alias, WHERE, &tbl.Col1, '=', P("foo"), ) fmt.Println(sb.String())
Output: SELECT bsq.users.Col1, u.Col1 FROM bsq.users u WHERE Col1=?
Types ¶
type CRUD ¶
type CRUD struct { CreateQuery Query ReadQuery Query UpdateQuery Query DeleteQuery Query // contains filtered or unexported fields }
func (*CRUD) Create ¶
func (*CRUD) CreateNamed ¶
func (*CRUD) Delete ¶
func (*CRUD) Update ¶
type Columner ¶
type Columner interface { SQLWriterTo Table() Tabler Q(qualifier string) Columner ColumnDefn() *Column }
Columner is an abstraction for model columns that can be written as SQL. E.g. Column is a Columner.
type Create ¶
Create writes an SQL statement that inserts data into a table and returns an automatically generated int64 id for the new row. This corresponds to the sql.Result.LastInsertId() feature, which—unfortunately—is not implemented by all sql drivers. Dialects for bsq shall find a reasonable implementation.
type DefnVisitor ¶
type Delete ¶
Example ¶
del := Delete{EqColumns: Cols(&testTable.ID)} stmt, _ := MustSQLString(DefaultDialect, 0, del) fmt.Println(stmt)
Output: DELETE FROM bsq.users WHERE ID=?
type Dialect ¶
type Dialect interface { ParamNamer QuoteName(defn string) (escaped string) CreateSQL(tbl *Table, idCol Columner, setCols []Columner) []interface{} QueryCreate(db sqlize.Querier, query string, args ...interface{}) (id int64, err error) UpsertSQL(tbl *Table, conflict, set []Columner) []interface{} }
var (
DefaultDialect Dialect
)
type IndexedParams ¶
type IndexedParams string
func (IndexedParams) ArgMapper ¶
func (_ IndexedParams) ArgMapper(ctx interface{}) func([]sql.NamedArg) []interface{}
func (IndexedParams) ParamName ¶
func (ip IndexedParams) ParamName(p P, ctx interface{}) (string, interface{})
type InitDefn ¶
type InitOpts ¶
type Insert ¶
type Insert struct {
Columns []Columner
}
Example ¶
ins := Insert{Columns: ColsOf(&testTable)} stmt, _ := MustSQLString(DefaultDialect, 0, ins) fmt.Println(stmt)
Output: INSERT INTO bsq.users (ID, Name, Age) VALUES (?, ?, ?)
type JoinEq ¶
Example ¶
join := JoinEq{From: &testTable.ID, To: &testTable.Name} stmt, _ := MustSQLString(DefaultDialect, 0, join) fmt.Println(stmt)
Output: JOIN bsq.users ON (ID=Name)
type NamedParams ¶
type NamedParams string
func (NamedParams) ArgMapper ¶
func (_ NamedParams) ArgMapper(ctx interface{}) func([]sql.NamedArg) []interface{}
func (NamedParams) ParamName ¶
func (np NamedParams) ParamName(p P, ctx interface{}) (string, interface{})
type ParamNamer ¶
type PositionalParams ¶
type PositionalParams string
func (PositionalParams) ArgMapper ¶
func (_ PositionalParams) ArgMapper(ctx interface{}) func([]sql.NamedArg) []interface{}
func (PositionalParams) ParamName ¶
func (pp PositionalParams) ParamName(p P, ctx interface{}) (string, interface{})
type Query ¶
type Query struct {
// contains filtered or unexported fields
}
func NewQuery ¶ added in v0.2.0
If d is nil, the dialect will be determined on first use.
func (*Query) Exec ¶ added in v0.2.0
func (*Query) ExecContext ¶ added in v0.2.0
func (*Query) ExecNamed ¶ added in v0.2.0
func (*Query) ExecNamedContext ¶ added in v0.2.0
func (*Query) Prepare ¶ added in v0.2.0
func (*Query) PrepareContext ¶ added in v0.2.0
func (*Query) Query ¶ added in v0.2.0
func (*Query) QueryContext ¶ added in v0.2.0
func (*Query) QueryNamed ¶ added in v0.2.0
func (*Query) QueryNamedContext ¶ added in v0.2.0
func (*Query) QueryRow ¶ added in v0.2.0
func (*Query) QueryRowContext ¶ added in v0.2.0
func (*Query) QueryRowNamed ¶ added in v0.2.0
func (*Query) QueryRowNamedContext ¶ added in v0.2.0
type Select ¶
Example ¶
sel := Select{ Columns: ColsOf(&testTable, &testTable.ID), EqColumns: Cols(&testTable.ID), } stmt, _ := MustSQLString(DefaultDialect, 0, sel) fmt.Println(stmt)
Output: SELECT Name, Age FROM bsq.users WHERE ID=?
type Table ¶
type Table struct { // The schema name of the declared table. If left empty it can be // set with InitDefns. Schema string // The table name. Name string // A default alias for SQL queries. Specific aliases can be // created with the Alias method. DefaultAlias string // contains filtered or unexported fields }
Table is used to declare the table data for the DB model replica. Replica are used to define SQL queries.
Example ¶
// Declare the table var tableDefn = struct { Table ID, Name, Age Column }{ Table: Table{Name: "users", DefaultAlias: "u"}, } // Fully initialize and set the schema name to "bsq" InitDefns("bsq", nil, &tableDefn) // Create a query query, _ := MustSQLString(DefaultDialect, Alias|Qualify, Select{EqColumns: Cols(&tableDefn.ID)}) fmt.Println(query)
Output: SELECT u.Name, u.Age FROM bsq.users u WHERE u.ID=?
func (*Table) Alias ¶
func (tbl *Table) Alias(alias string) TableAlias
type TableAlias ¶
type TableAlias struct {
// contains filtered or unexported fields
}
func (TableAlias) Q ¶
func (a TableAlias) Q(col Columner) Columner
func (TableAlias) TableData ¶
func (a TableAlias) TableData() *Table
func (TableAlias) WriteTo ¶
func (a TableAlias) WriteTo(w io.Writer, ctx *writeContext) error
type Tabler ¶
type Tabler interface { SQLWriterTo TableData() *Table }
Tabler is an abstraction for model tables that can be written as SQL. E.g. Table is a Tabler.
type Update ¶
Example ¶
upd := Update{ Set: ColsOf(&testTable, &testTable.ID), EqColumns: Cols(&testTable.ID), } stmt, _ := MustSQLString(DefaultDialect, 0, upd) fmt.Println(stmt)
Output: UPDATE bsq.users SET Name=?, Age=? WHERE ID=?
type Upsert ¶
type WriteFlag ¶
type WriteFlag uint
const ( // Write a Table's DefaultAlias to SQL statements if the alias is defined. Alias WriteFlag = (1 << iota) // Write qualified column names to SQL statements. If the table has an alias // the column is qualified with that alias. Otherwiese it is qualified with // the table name. Qualify // Wirte aliases and qualified columns to SQL statements. Explicit = Alias | Qualify )