Documentation
¶
Overview ¶
Package query builds parameterized SQL statements from structured, composable query state.
Query.Build emits ANSI SQL: "?" placeholders, double-quoted identifiers, and OFFSET n ROWS / FETCH FIRST n ROWS ONLY for paging. Query.BuildFor compiles for a specific Dialect: Postgres numbers placeholders, MySQL quotes identifiers with backticks, and MySQL, SQLite, and Postgres page with LIMIT and OFFSET. An OFFSET without a LIMIT renders as LIMIT 18446744073709551615 OFFSET n on MySQL, LIMIT -1 OFFSET n on SQLite, and a bare OFFSET n on Postgres. SQLite otherwise shares ANSI placeholders and identifier quoting. Use SelectRaw and HavingRaw for aggregates and other expressions the structured API cannot express.
Table and FromRaw publish immutable Query values from ordered callbacks on one mutable Builder. Settings return the same draft. Query.Derive starts an independent draft from a base; no callbacks preserves its sticky errors. Supplied nil callbacks panic, while expanded nil or empty slices omit edits. Publication detaches owned containers and retained root and predicate drafts, not arbitrary SQL argument objects. Only published values compile or execute. Values become positional bindings; identifiers are validated and quoted.
Example:
import "arkoren.dev/foundation/query"
users := query.Table("users", func(statement *query.Builder) {
statement.Where("active", true)
})
statement, err := users.BuildFor(query.Postgres)
if err != nil {
return err
}
_ = statement.SQL()
Raw sources, statements, and conditions ¶
FromRaw starts a SELECT from a subquery, rendered as FROM (sql) AS alias with the alias set by Builder.As ("t" by default). Its arguments bind before the outer query's. WhereRaw and OrWhereRaw add verbatim conditions like HavingRaw. Raw wraps a whole statement for Exec, Select, and Find calls the builder cannot express; it satisfies Statementer but is opaque.
Mutation features ¶
Builder.Returning appends RETURNING on Postgres and SQLite and fails with ErrUnsupported elsewhere. Builder.OnConflict followed by DoUpdate or DoNothing builds an upsert: ON CONFLICT on Postgres and SQLite, ON DUPLICATE KEY UPDATE with VALUES() or INSERT IGNORE on MySQL, and ErrUnsupported on ANSI. Builder.Increment, Builder.Decrement, and Builder.SetRaw add computed assignments that compose with Builder.Update. Builder.ForUpdate, Builder.ForShare, Builder.SkipLocked, and Builder.NoWait lock selected rows on Postgres and MySQL 8; SQLite and ANSI fail with ErrUnsupported. PostgreSQL row locks also reject DISTINCT, GROUP BY, and HAVING. SQLite rejects qualified written column names in INSERT, UPDATE, and conflict clauses with ErrUnsupported; qualified tables and read references remain available. Duplicate DoUpdate assignments are invalid on every dialect.
Introspection and pagination ¶
Query.Inspect returns a read-only Spec copy of the query's table, orders, paging, and shape: its Kind (SELECT, INSERT, UPDATE, or DELETE) and its Order list. Query.Count derives the matching COUNT(*) query, wrapping grouped, DISTINCT, HAVING, and raw-sourced queries in a subquery. Query.Seek adds keyset (cursor) pagination filters from the query's orders. Both transformations leave the base unchanged. Limit and Offset reject negative inputs. Limit(0) requests no rows rather than an unbounded result; Offset(0) remains an explicit zero in Inspect.
Scopes and exists ¶
Builder.Scope groups the entire nonempty user filter chain, then ANDs each scope as its own group. Neither a later OrWhere nor a disjunction in Builder.WhereRaw can bypass a scope. Scopes also apply to UPDATE, DELETE, and Query.Count, and satisfy the Builder.Unfiltered guard. Each scope receives its own PredicateBuilder; an empty scope is a no-op, nil panics. Ordinary predicate groups share one restricted draft across their callbacks; validate the combined result, which must not be empty. Raw text must remain trusted, constant SQL with bound values: grouping does not validate arbitrary SQL. Without scopes, raw precedence is unchanged. Builder.WhereExists and its NOT and OR variants take a published SELECT query, correlated with Builder.WhereColumn. MySQL fails with error 1093 when an UPDATE or DELETE subquery reads the target table.
UPDATE and DELETE statements without a WHERE clause are rejected unless Builder.Unfiltered is called, which explicitly allows them to affect every row. INSERT, UPDATE, and DELETE builders that carry clauses they cannot render, such as joins, grouping, ordering, limits, or DISTINCT, fail at Build instead of silently dropping them. An empty WhereIn list is false; an empty WhereNotIn list is true. Neither binds arguments. A negative membership with no values therefore does not restrict the affected rows, even though it satisfies the WHERE guard.
Raw expressions and literal safety ¶
SelectRaw and HavingRaw render their expression verbatim and cannot be validated: never build one from user input; pass values as arguments. A raw expression that accepts arguments, such as HavingRaw, binds them through "?" placeholders, which BuildFor rewrites to the dialect placeholder. SelectRaw takes no arguments and copies its expression without scanning: write a single "?" for a PostgreSQL JSON operator there, not "??". Placeholders are counted outside '...', "...", and `...` quotes, -- line comments, and /* */ block comments. The number of placeholders must equal the number of arguments, and "??" writes a literal "?" (for example the PostgreSQL jsonb operator). An empty expression is an error. Block comments nest on ANSI and PostgreSQL; MySQL and SQLite end a block comment at the first */. PostgreSQL line comments end at CR or LF; other dialects use LF. Backslash escapes inside MySQL strings and PostgreSQL dollar quoting are not understood, so keep such text out of raw expressions or pass it as an argument. MySQL recognizes -- as a comment only before whitespace or a control character; otherwise the scanner retains the two subtraction/minus tokens. Structured IS DISTINCT FROM and IS NOT DISTINCT FROM comparisons fail with ErrUnsupported on MySQL; ILIKE and NOT ILIKE require PostgreSQL. These dialect checks also apply to column comparisons and join conditions.
DDL lives in the subpackage arkoren.dev/foundation/query/schema, which compiles to argument-free statements through Raw and reports dialect limits with ErrUnsupported. Dialect.TransactionalDDL tells whether DDL rolls back, and Dialect.Quote quotes one identifier part for a dialect.
Structs and named parameters ¶
Values turns a struct into a column-to-value map for Insert and Update, and Targets returns the scan destinations of a result row for a struct, with one set of field rules (see Values). RawNamed writes caller SQL with :name placeholders instead of ?, so a map from Values can bind it. Only and Except retain copied column names. Only() adds no restriction; Except() drops nothing. Values retains field objects rather than converting them: a nonpointer embedded T with only a pointer Valuer maps its fields, whereas an embedded *T implementing Valuer stays one bound pointer value.
Package query only builds statements. Database execution, result scanning, and transactions belong to the database contract and its drivers.
Index ¶
- Variables
- func Targets(dest any, columns []string) ([]any, error)
- func Values(v any, opts ...ValuesOption) map[string]any
- type Builder
- func (statement *Builder) AddSelect(columns ...string) *Builder
- func (statement *Builder) As(alias string) *Builder
- func (statement *Builder) CrossJoin(table string) *Builder
- func (statement *Builder) Decrement(column string, by any) *Builder
- func (statement *Builder) Delete() *Builder
- func (statement *Builder) Distinct() *Builder
- func (statement *Builder) ForShare() *Builder
- func (statement *Builder) ForUpdate() *Builder
- func (statement *Builder) From(name string) *Builder
- func (statement *Builder) GroupBy(columns ...string) *Builder
- func (statement *Builder) Having(column string, args ...any) *Builder
- func (statement *Builder) HavingRaw(expression string, args ...any) *Builder
- func (statement *Builder) Increment(column string, by any) *Builder
- func (statement *Builder) Insert[M ~map[string]V, V any](values M) *Builder
- func (statement *Builder) InsertRows[M ~map[string]V, V any](values ...M) *Builder
- func (statement *Builder) Join(table, first, operator, second string) *Builder
- func (statement *Builder) Latest(column string) *Builder
- func (statement *Builder) LeftJoin(table, first, operator, second string) *Builder
- func (statement *Builder) Limit(limit int) *Builder
- func (statement *Builder) NoWait() *Builder
- func (statement *Builder) Offset(offset int) *Builder
- func (statement *Builder) Oldest(column string) *Builder
- func (statement *Builder) OnConflict(columns ...string) *Conflict
- func (statement *Builder) OrHaving(column string, args ...any) *Builder
- func (statement *Builder) OrWhere(column string, args ...any) *Builder
- func (statement *Builder) OrWhereBetween(column string, minimum, maximum any) *Builder
- func (statement *Builder) OrWhereColumn(first string, args ...string) *Builder
- func (statement *Builder) OrWhereExists(sub Query) *Builder
- func (statement *Builder) OrWhereGroup(configure ...PredicateBuilderFunc) *Builder
- func (statement *Builder) OrWhereIn[T any](column string, values ...T) *Builder
- func (statement *Builder) OrWhereNotBetween(column string, minimum, maximum any) *Builder
- func (statement *Builder) OrWhereNotExists(sub Query) *Builder
- func (statement *Builder) OrWhereNotGroup(configure ...PredicateBuilderFunc) *Builder
- func (statement *Builder) OrWhereNotIn[T any](column string, values ...T) *Builder
- func (statement *Builder) OrWhereNotNull(column string) *Builder
- func (statement *Builder) OrWhereNull(column string) *Builder
- func (statement *Builder) OrWhereRaw(expression string, args ...any) *Builder
- func (statement *Builder) OrderBy(column string) *Builder
- func (statement *Builder) OrderByDesc(column string) *Builder
- func (statement *Builder) Returning(columns ...string) *Builder
- func (statement *Builder) RightJoin(table, first, operator, second string) *Builder
- func (statement *Builder) Scope(scopes ...Scope) *Builder
- func (statement *Builder) Select(columns ...string) *Builder
- func (statement *Builder) SelectRaw(expression string) *Builder
- func (statement *Builder) SetRaw(column, expression string, args ...any) *Builder
- func (statement *Builder) SkipLocked() *Builder
- func (statement *Builder) Unfiltered() *Builder
- func (statement *Builder) Update[M ~map[string]V, V any](values M) *Builder
- func (statement *Builder) Where(column string, args ...any) *Builder
- func (statement *Builder) WhereBetween(column string, minimum, maximum any) *Builder
- func (statement *Builder) WhereColumn(first string, args ...string) *Builder
- func (statement *Builder) WhereExists(sub Query) *Builder
- func (statement *Builder) WhereGroup(configure ...PredicateBuilderFunc) *Builder
- func (statement *Builder) WhereIn[T any](column string, values ...T) *Builder
- func (statement *Builder) WhereNotBetween(column string, minimum, maximum any) *Builder
- func (statement *Builder) WhereNotExists(sub Query) *Builder
- func (statement *Builder) WhereNotGroup(configure ...PredicateBuilderFunc) *Builder
- func (statement *Builder) WhereNotIn[T any](column string, values ...T) *Builder
- func (statement *Builder) WhereNotNull(column string) *Builder
- func (statement *Builder) WhereNull(column string) *Builder
- func (statement *Builder) WhereRaw(expression string, args ...any) *Builder
- type BuilderFunc
- type Conflict
- type Dialect
- type Kind
- type Order
- type PredicateBuilder
- func (predicate *PredicateBuilder) OrWhere(column string, args ...any) *PredicateBuilder
- func (predicate *PredicateBuilder) OrWhereBetween(column string, minimum, maximum any) *PredicateBuilder
- func (predicate *PredicateBuilder) OrWhereColumn(first string, args ...string) *PredicateBuilder
- func (predicate *PredicateBuilder) OrWhereExists(sub Query) *PredicateBuilder
- func (predicate *PredicateBuilder) OrWhereGroup(configure ...PredicateBuilderFunc) *PredicateBuilder
- func (predicate *PredicateBuilder) OrWhereIn[T any](column string, values ...T) *PredicateBuilder
- func (predicate *PredicateBuilder) OrWhereNotBetween(column string, minimum, maximum any) *PredicateBuilder
- func (predicate *PredicateBuilder) OrWhereNotExists(sub Query) *PredicateBuilder
- func (predicate *PredicateBuilder) OrWhereNotGroup(configure ...PredicateBuilderFunc) *PredicateBuilder
- func (predicate *PredicateBuilder) OrWhereNotIn[T any](column string, values ...T) *PredicateBuilder
- func (predicate *PredicateBuilder) OrWhereNotNull(column string) *PredicateBuilder
- func (predicate *PredicateBuilder) OrWhereNull(column string) *PredicateBuilder
- func (predicate *PredicateBuilder) OrWhereRaw(expression string, args ...any) *PredicateBuilder
- func (predicate *PredicateBuilder) Where(column string, args ...any) *PredicateBuilder
- func (predicate *PredicateBuilder) WhereBetween(column string, minimum, maximum any) *PredicateBuilder
- func (predicate *PredicateBuilder) WhereColumn(first string, args ...string) *PredicateBuilder
- func (predicate *PredicateBuilder) WhereExists(sub Query) *PredicateBuilder
- func (predicate *PredicateBuilder) WhereGroup(configure ...PredicateBuilderFunc) *PredicateBuilder
- func (predicate *PredicateBuilder) WhereIn[T any](column string, values ...T) *PredicateBuilder
- func (predicate *PredicateBuilder) WhereNotBetween(column string, minimum, maximum any) *PredicateBuilder
- func (predicate *PredicateBuilder) WhereNotExists(sub Query) *PredicateBuilder
- func (predicate *PredicateBuilder) WhereNotGroup(configure ...PredicateBuilderFunc) *PredicateBuilder
- func (predicate *PredicateBuilder) WhereNotIn[T any](column string, values ...T) *PredicateBuilder
- func (predicate *PredicateBuilder) WhereNotNull(column string) *PredicateBuilder
- func (predicate *PredicateBuilder) WhereNull(column string) *PredicateBuilder
- func (predicate *PredicateBuilder) WhereRaw(expression string, args ...any) *PredicateBuilder
- type PredicateBuilderFunc
- type Query
- func (query Query) Build() (Statement, error)
- func (query Query) BuildFor(dialect Dialect) (Statement, error)
- func (query Query) Count() Query
- func (query Query) Derive(configure ...BuilderFunc) Query
- func (query Query) Inspect() Spec
- func (query Query) IsSelect() bool
- func (query Query) Seek(values []any, backward bool) Query
- type RawStatement
- type Scope
- type Spec
- type Statement
- type Statementer
- type ValuesOption
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ( // ANSI uses ? placeholders and double-quoted identifiers. [Query.Build] // compiles for ANSI. // // Example: // // statement, err := query.Table("users").BuildFor(query.ANSI) // if err != nil { // return err // } // _ = statement.SQL() ANSI = Dialect{ // contains filtered or unexported fields } // SQLite uses ? placeholders and double-quoted identifiers. // // Example: // // statement, err := query.Table("users").BuildFor(query.SQLite) // if err != nil { // return err // } // _ = statement.SQL() SQLite = Dialect{ // contains filtered or unexported fields } // Postgres uses numbered $n placeholders and double-quoted identifiers. // // Example: // // statement, err := query.Table("users", func(statement *query.Builder) { // statement.Where("id", 7) // }).BuildFor(query.Postgres) // if err != nil { // return err // } // _ = statement.Args() Postgres = Dialect{ // contains filtered or unexported fields } // MySQL uses ? placeholders and backtick-quoted identifiers, so it works // without the ANSI_QUOTES SQL mode. // // Example: // // statement, err := query.Table("users").BuildFor(query.MySQL) // if err != nil { // return err // } // _ = statement.SQL() MySQL = Dialect{ // contains filtered or unexported fields } )
var ( // ErrUnknownColumn reports that [Targets] found a result column with no // field in the destination struct. // // Example: // // var user struct{ ID int } // _, err := query.Targets(&user, []string{"missing"}) // _ = errors.Is(err, query.ErrUnknownColumn) ErrUnknownColumn = errors.New("column has no field in destination") // ErrInvalidTarget reports that [Targets] cannot scan into its // destination: it is not a non-nil pointer, or it is a single value and // the result does not have exactly one column. // // Example: // // _, err := query.Targets(7, []string{"id"}) // _ = errors.Is(err, query.ErrInvalidTarget) ErrInvalidTarget = errors.New("invalid scan destination") )
var ErrInvalidQuery = errors.New("invalid query")
ErrInvalidQuery identifies validation or compilation failures from Query and RawStatement. Build errors wrap the underlying cause as well as this sentinel, so callers can classify them without depending on diagnostic text.
Example:
_, err := query.Table("invalid table").Build()
_ = errors.Is(err, query.ErrInvalidQuery)
var ErrUnsupported = errors.New("unsupported by dialect")
ErrUnsupported indicates that a dialect cannot express a statement or clause. Build errors that carry it also match ErrInvalidQuery.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.ForUpdate()
})
_, err := users.BuildFor(query.SQLite)
_ = errors.Is(err, query.ErrUnsupported)
Functions ¶
func Targets ¶
Targets returns the scan destinations for one result row: one pointer per entry of columns, in order, for use with database/sql.Rows.Scan.
dest is a non-nil pointer to a struct, whose fields follow the rules of Values, or to a single value (a number, a string, time.Time, a database/sql.Scanner, or a struct without mapped fields), which needs exactly one column. A column without a field fails with ErrUnknownColumn; fields without a column are left untouched. Nil embedded struct pointers on the way to a field are allocated. Any other destination fails with ErrInvalidTarget.
Example:
var user struct {
ID int
Name string
}
targets, err := query.Targets(&user, []string{"id", "name"})
if err != nil {
return err
}
if err := rows.Scan(targets...); err != nil {
return err
}
_ = user.Name
Example ¶
ExampleTargets handles column-mapping failure before consuming destinations. Writing through those destinations models the values supplied by Rows.Scan.
package main
import (
"fmt"
"arkoren.dev/foundation/query"
)
func main() {
var user struct {
// ID receives the result's id column.
ID int
// Name receives the result's name column.
Name string
}
targets, err := query.Targets(&user, []string{"id", "name"})
if err != nil {
fmt.Println(err)
return
}
*targets[0].(*int) = 7
*targets[1].(*string) = "Ada"
fmt.Println(user.ID, user.Name)
}
Output: 7 Ada
func Values ¶
func Values(v any, opts ...ValuesOption) map[string]any
Values returns the column values of a struct as a map from column name to value, ready for Builder.Insert, Builder.Update, or RawNamed. v is a struct or a non-nil pointer to one; Values panics on anything else, which is a programming error.
The columns follow one set of rules, shared with Targets and with cursor pagination:
- The column is the db tag name, the text before the first comma. An empty name uses the lowercased field name, so an untagged exported field "CreatedAt" is the column "createdat". db:"-" skips the field.
- Unexported fields are skipped.
- An untagged embedded struct is flattened into the columns of its parent. A field of the outer struct wins over one with the same column in an embedded struct; the first discovered field wins at equal depth.
- A field whose type implements database/sql/driver.Valuer or, through a pointer, database/sql.Scanner, and time.Time, is one column and is never flattened. The value is returned as is, so the database driver converts it. Valuer detection uses the declared field type: if only *T implements Valuer, embed T to map its fields or use *T to bind the pointer itself. Scanner and time.Time detection still take precedence over flattening.
A nil embedded struct pointer yields a typed nil pointer for each of its columns, which a database driver writes as NULL. Values are not converted: a pointer field is returned as the pointer.
Example:
user := struct {
ID int
Name string
}{ID: 7, Name: "Ada"}
values := query.Values(user, query.Except("id"))
statement, err := query.Table("users", func(statement *query.Builder) {
statement.Where("id", user.ID)
statement.Update(values)
}).Build()
if err != nil {
return err
}
_ = statement.SQL()
Types ¶
type Builder ¶
type Builder struct {
// contains filtered or unexported fields
}
Builder declares mutable SQL settings during construction. Settings return this same draft. Drafts cannot compile or execute, and are not concurrency-safe. Publication detaches their owned state.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.Where("active", true)
})
_ = users
func (*Builder) AddSelect ¶
AddSelect appends selected columns. It mutates this draft and returns the same receiver. Invalid settings remain recorded until the published query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.AddSelect("email")
})
_ = users
func (*Builder) As ¶
As names the subquery source of a FromRaw query. It is an error on builders without a raw source: plain tables cannot be aliased, and a table name with an alias, such as "posts p", is an invalid identifier. It mutates this draft and returns the same receiver. Invalid settings remain recorded until the published query is compiled.
Example:
users := query.FromRaw(query.Raw("SELECT id FROM users"), func(statement *query.Builder) {
statement.As("members")
})
_ = users
func (*Builder) CrossJoin ¶
CrossJoin appends a cross join. It mutates this draft and returns the same receiver. Invalid settings remain recorded until the published query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.CrossJoin("roles")
})
_ = users
func (*Builder) Decrement ¶
Decrement changes the query into an update statement that subtracts by from column, rendered as column = column - ?. See Builder.Increment. It mutates this draft and returns the same receiver. Invalid settings remain recorded until the published query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.Where("id", 7)
statement.Decrement("credits", 1)
})
_ = users
func (*Builder) Delete ¶
Delete changes the query into a delete statement. It mutates this draft and returns the same receiver. Invalid settings remain recorded until the published query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.Where("id", 7)
statement.Delete()
})
_ = users
func (*Builder) Distinct ¶
Distinct makes the select statement return distinct rows. It mutates this draft and returns the same receiver. Invalid settings remain recorded until the published query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.Distinct()
})
_ = users
func (*Builder) ForShare ¶
ForShare locks the selected rows against concurrent updates while allowing other readers to lock them, rendered as FOR SHARE. MySQL needs 8.0 or later. See Builder.ForUpdate for dialect support. It mutates this draft and returns the same receiver. Invalid settings remain recorded until the published query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.ForShare()
})
_ = users
func (*Builder) ForUpdate ¶
ForUpdate locks the selected rows against concurrent writes until the transaction ends, rendered as FOR UPDATE. Locking is supported on Postgres and MySQL; SQLite has no row locks and ANSI has no portable form, so those dialects fail with ErrUnsupported. Only SELECT accepts it. PostgreSQL also rejects DISTINCT, GROUP BY, and HAVING with a row lock, returning ErrUnsupported. Raw expressions remain database-validated. It mutates this draft and returns the same receiver. Invalid settings remain recorded until the published query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.ForUpdate()
})
_ = users
func (*Builder) From ¶
From sets the query's source table. It mutates this draft and returns the same receiver. Invalid settings remain recorded until the published query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.From("accounts")
})
_ = users
func (*Builder) GroupBy ¶
GroupBy appends grouping columns. It mutates this draft and returns the same receiver. Invalid settings remain recorded until the published query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.Select("role")
statement.GroupBy("role")
})
_ = users
func (*Builder) Having ¶
Having appends an AND aggregate filter. Use a compatible grouped or aggregate selection; the database validates grouping semantics. Comparison arguments follow Builder.Where. It mutates this draft and returns the same receiver. Invalid settings remain recorded until the published query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.Select("role")
statement.GroupBy("role")
statement.Having("role", "admin")
})
_ = users
func (*Builder) HavingRaw ¶
HavingRaw appends an AND aggregate filter rendered verbatim, such as "COUNT(*) > ?", with args bound to its placeholders. An empty expression is an error, and the number of ? placeholders must equal len(args); write ?? for a literal question mark. Never pass user input as expression: it is not escaped. It mutates this draft and returns the same receiver. Invalid settings remain recorded until the published query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.SelectRaw("COUNT(*) AS total")
statement.HavingRaw("COUNT(*) > ?", 5)
})
_ = users
Example ¶
ExampleBuilder_HavingRaw selects an aggregate before filtering its single group, rather than retaining the incompatible default wildcard selection.
package main
import (
"fmt"
"arkoren.dev/foundation/query"
)
func main() {
statement, err := query.Table("users", func(statement *query.Builder) {
statement.SelectRaw("COUNT(*) AS total")
statement.HavingRaw("COUNT(*) > ?", 5)
}).BuildFor(query.Postgres)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(statement.SQL(), statement.Args())
}
Output: SELECT COUNT(*) AS total FROM "users" HAVING COUNT(*) > $1 [5]
func (*Builder) Increment ¶
Increment changes the query into an update statement that adds by to column, rendered as column = column + ?. It composes with Builder.Update and other expressions; assigning one column twice fails at Build. A nil amount is an error. It mutates this draft and returns the same receiver. Invalid settings remain recorded until the published query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.Where("id", 7)
statement.Increment("visits", 1)
})
_ = users
func (*Builder) Insert ¶
Insert changes the query into a single-row insert statement. It mutates this draft and returns the same receiver. Invalid settings remain recorded until the published query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.Insert(map[string]any{"name": "Ada"})
})
_ = users
func (*Builder) InsertRows ¶
InsertRows changes the query into a multi-row insert statement. It mutates this draft and returns the same receiver. Invalid settings remain recorded until the published query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.InsertRows(map[string]any{"name": "Ada"}, map[string]any{"name": "Grace"})
})
_ = users
func (*Builder) Join ¶
Join appends an inner join with a column comparison. It mutates this draft and returns the same receiver. Invalid settings remain recorded until the published query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.Join("accounts", "accounts.id", "=", "users.account_id")
})
_ = users
func (*Builder) Latest ¶
Latest orders descending by column. It mutates this draft and returns the same receiver. Invalid settings remain recorded until the published query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.Latest("created_at")
})
_ = users
func (*Builder) LeftJoin ¶
LeftJoin appends a left join with a column comparison. It mutates this draft and returns the same receiver. Invalid settings remain recorded until the published query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.LeftJoin("accounts", "accounts.id", "=", "users.account_id")
})
_ = users
func (*Builder) Limit ¶
Limit restricts the maximum number of returned rows. Only SELECT accepts it; INSERT, UPDATE, and DELETE reject it at Build. Omission leaves the result unbounded. Zero explicitly requests no rows; negative input records an error that later calls cannot repair. It mutates this draft and returns the same receiver. Invalid settings remain recorded until the published query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.Limit(20)
})
_ = users
func (*Builder) NoWait ¶
NoWait makes a locking SELECT fail at once when a row is locked by another transaction. It requires Builder.ForUpdate or Builder.ForShare and cannot combine with Builder.SkipLocked. It mutates this draft and returns the same receiver. Invalid settings remain recorded until the published query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.ForUpdate()
statement.NoWait()
})
_ = users
func (*Builder) Offset ¶
Offset skips rows before returning results. It may be used without Limit; each dialect then emits its own unbounded-limit form. Only SELECT accepts it. Omission adds no offset clause. Zero explicitly skips no rows and remains visible through Inspect; negative input records a sticky validation error. It mutates this draft and returns the same receiver. Invalid settings remain recorded until the published query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.Offset(40)
})
_ = users
func (*Builder) Oldest ¶
Oldest orders ascending by column. It mutates this draft and returns the same receiver. Invalid settings remain recorded until the published query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.Oldest("created_at")
})
_ = users
func (*Builder) OnConflict ¶
OnConflict begins upsert handling for an INSERT, naming the columns of the unique constraint that may conflict. Postgres and SQLite use them as the ON CONFLICT target and require a nonempty target for Conflict.DoUpdate. MySQL reacts to any unique key and ignores the target, including an empty one. Conflict.DoNothing permits omission on all supported dialects; upserts are unsupported on ANSI. It returns an attached child draft. Finish with DoUpdate or DoNothing to install conflict handling; either completion returns this parent Builder.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.Insert(map[string]any{"email": "ada@example.test"})
statement.OnConflict("email").DoNothing()
})
_ = users
func (*Builder) OrHaving ¶
OrHaving appends an OR aggregate filter. Selection and grouping requirements are those of Builder.Having. It mutates this draft and returns the same receiver. Invalid settings remain recorded until the published query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.Select("role")
statement.GroupBy("role")
statement.Having("role", "admin")
statement.OrHaving("role", "owner")
})
_ = users
Example ¶
ExampleBuilder_OrHaving filters a compatible grouped selection and appends the second group filter with OR while retaining binding order.
package main
import (
"fmt"
"arkoren.dev/foundation/query"
)
func main() {
statement, err := query.Table("users", func(statement *query.Builder) {
statement.Select("role")
statement.GroupBy("role")
statement.Having("role", "admin")
statement.OrHaving("role", "owner")
}).BuildFor(query.Postgres)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(statement.SQL(), statement.Args())
}
Output: SELECT "role" FROM "users" GROUP BY "role" HAVING "role" = $1 OR "role" = $2 [admin owner]
func (*Builder) OrWhere ¶
OrWhere appends an OR value comparison. Argument forms and nil handling follow Builder.Where. It mutates this draft and returns the same receiver. Invalid settings remain recorded until the published query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.OrWhere("role", "admin")
})
_ = users
func (*Builder) OrWhereBetween ¶
OrWhereBetween appends an OR inclusive range comparison. It mutates this draft and returns the same receiver. Invalid settings remain recorded until the published query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.OrWhereBetween("score", 1, 10)
})
_ = users
func (*Builder) OrWhereColumn ¶
OrWhereColumn appends an OR comparison between columns. It mutates this draft and returns the same receiver. Invalid settings remain recorded until the published query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.OrWhereColumn("created_at", "<", "updated_at")
})
_ = users
func (*Builder) OrWhereExists ¶
OrWhereExists appends an OR EXISTS (sub) filter. See Builder.WhereExists.
Example:
owned := query.Table("owners", func(statement *query.Builder) {
statement.WhereColumn("owners.post_id", "posts.id")
})
_ = query.Table("posts", func(statement *query.Builder) {
statement.Where("public", true)
statement.OrWhereExists(owned)
})
func (*Builder) OrWhereGroup ¶
func (statement *Builder) OrWhereGroup(configure ...PredicateBuilderFunc) *Builder
OrWhereGroup appends a parenthesized OR predicate group. It mutates this draft and returns the same receiver. Invalid settings remain recorded until the published query is compiled. Callbacks run directly once, synchronously in order on one restricted draft; nil entries panic. The final group must contain predicates, even when callbacks are omitted or an expanded empty slice is supplied.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.OrWhereGroup(func(predicate *query.PredicateBuilder) {
predicate.Where("active", true)
})
})
_ = users
func (*Builder) OrWhereIn ¶
OrWhereIn appends an OR membership comparison. Empty lists are always false and bind no arguments, as in Builder.WhereIn. It mutates this draft and returns the same receiver. Invalid settings remain recorded until the published query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.OrWhereIn("role", "admin", "editor")
})
_ = users
func (*Builder) OrWhereNotBetween ¶
OrWhereNotBetween appends an OR negative range comparison. It mutates this draft and returns the same receiver. Invalid settings remain recorded until the published query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.OrWhereNotBetween("score", 1, 10)
})
_ = users
func (*Builder) OrWhereNotExists ¶
OrWhereNotExists appends an OR NOT EXISTS (sub) filter. See Builder.WhereExists.
Example:
hidden := query.Table("hides", func(statement *query.Builder) {
statement.WhereColumn("hides.post_id", "posts.id")
})
_ = query.Table("posts", func(statement *query.Builder) {
statement.Where("public", true)
statement.OrWhereNotExists(hidden)
})
func (*Builder) OrWhereNotGroup ¶
func (statement *Builder) OrWhereNotGroup(configure ...PredicateBuilderFunc) *Builder
OrWhereNotGroup appends a negated OR predicate group. It mutates this draft and returns the same receiver. Invalid settings remain recorded until the published query is compiled. Callbacks run directly once, synchronously in order on one restricted draft; nil entries panic. The final group must contain predicates, even when callbacks are omitted or an expanded empty slice is supplied.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.OrWhereNotGroup(func(predicate *query.PredicateBuilder) {
predicate.Where("active", true)
})
})
_ = users
func (*Builder) OrWhereNotIn ¶
OrWhereNotIn appends an OR negative membership comparison. Empty lists are always true and bind no arguments, as in Builder.WhereNotIn. It mutates this draft and returns the same receiver. Invalid settings remain recorded until the published query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.OrWhereNotIn("role", "guest")
})
_ = users
func (*Builder) OrWhereNotNull ¶
OrWhereNotNull appends an OR not-null comparison. It mutates this draft and returns the same receiver. Invalid settings remain recorded until the published query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.OrWhereNotNull("email")
})
_ = users
func (*Builder) OrWhereNull ¶
OrWhereNull appends an OR null comparison. It mutates this draft and returns the same receiver. Invalid settings remain recorded until the published query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.OrWhereNull("deleted_at")
})
_ = users
func (*Builder) OrWhereRaw ¶
OrWhereRaw appends an OR filter rendered verbatim. See Builder.WhereRaw. It mutates this draft and returns the same receiver. Invalid settings remain recorded until the published query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.OrWhereRaw("lower(email) = ?", "ada@example.test")
})
_ = users
func (*Builder) OrderBy ¶
OrderBy appends ascending ordering for column. It mutates this draft and returns the same receiver. Invalid settings remain recorded until the published query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.OrderBy("id")
})
_ = users
func (*Builder) OrderByDesc ¶
OrderByDesc appends descending ordering for column. It mutates this draft and returns the same receiver. Invalid settings remain recorded until the published query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.OrderByDesc("created_at")
})
_ = users
func (*Builder) Returning ¶
Returning asks INSERT, UPDATE, and DELETE to return the listed columns, such as "id" or "*". It is supported on Postgres and SQLite; building for MySQL or ANSI, or for a SELECT, fails (ANSI and MySQL with ErrUnsupported). Execute such statements as queries, not exec calls. It mutates this draft and returns the same receiver. Invalid settings remain recorded until the published query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.Insert(map[string]any{"name": "Ada"})
statement.Returning("id")
})
_ = users
func (*Builder) RightJoin ¶
RightJoin appends a right join with a column comparison. It mutates this draft and returns the same receiver. Invalid settings remain recorded until the published query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.RightJoin("accounts", "accounts.id", "=", "users.account_id")
})
_ = users
func (*Builder) Scope ¶
Scope adds each scope's predicates as a parenthesized group that is ANDed after the entire nonempty user predicate chain, itself parenthesized when the statement compiles. Thus an OR in WhereRaw or a later OrWhere cannot bypass a scope. Raw expressions must still be trusted SQL with bound values; grouping does not validate or sandbox arbitrary SQL. Scopes also apply to UPDATE, DELETE, and Query.Count, and satisfy the guard that Builder.Unfiltered lifts. Each callback receives an independent restricted draft, once in order. No callbacks or an empty scope adds nothing; supplied nil callbacks panic. The restricted surface permits only predicates, and INSERT rejects scopes. Publication detaches retained predicate drafts.
Example:
active := func(predicate *query.PredicateBuilder) {
predicate.Where("active", true)
}
statement, err := query.Table("users", func(statement *query.Builder) {
statement.Where("role", "admin")
statement.Scope(active)
}).Build()
if err != nil {
return err
}
_ = statement.SQL()
func (*Builder) Select ¶
Select replaces the selected columns. An empty selection compiles as *. It mutates this draft and returns the same receiver. Invalid settings remain recorded until the published query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.Select("id", "name")
})
_ = users
func (*Builder) SelectRaw ¶
SelectRaw appends expression, such as "COUNT(*) AS total", to the selected columns verbatim. An empty expression is an error. Never pass user input: it is not escaped. Unlike Builder.HavingRaw, SelectRaw takes no arguments and does not scan placeholders: a ? is copied as written, so use the single ? for PostgreSQL's JSON operator, not the ?? binding escape. It mutates this draft and returns the same receiver. Invalid settings remain recorded until the published query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.SelectRaw("COUNT(*) AS total")
})
_ = users
func (*Builder) SetRaw ¶
SetRaw changes the query into an update statement that assigns the SQL expression to column, such as SetRaw("seen_at", "COALESCE(seen_at, ?)", now). Placeholder rules follow Builder.HavingRaw. Never pass user input as expression. It mutates this draft and returns the same receiver. Invalid settings remain recorded until the published query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.Where("id", 7)
statement.SetRaw("visits", "visits + ?", 1)
})
_ = users
func (*Builder) SkipLocked ¶
SkipLocked makes a locking SELECT skip rows another transaction has locked. It requires Builder.ForUpdate or Builder.ForShare and cannot combine with Builder.NoWait. It mutates this draft and returns the same receiver. Invalid settings remain recorded until the published query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.ForUpdate()
statement.SkipLocked()
})
_ = users
func (*Builder) Unfiltered ¶
Unfiltered allows an UPDATE or DELETE without a WHERE clause, affecting every row. Without it, Build rejects such statements. It has no effect on SELECT or INSERT. It mutates this draft and returns the same receiver. Invalid settings remain recorded until the published query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.Delete()
statement.Unfiltered()
})
_ = users
func (*Builder) Update ¶
Update changes the query into an update statement. It replaces earlier Update values but keeps expressions added by Builder.Increment, Builder.Decrement, and Builder.SetRaw. It mutates this draft and returns the same receiver. Invalid settings remain recorded until the published query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.Where("id", 7)
statement.Update(map[string]any{"name": "Ada"})
})
_ = users
func (*Builder) Where ¶
Where appends an AND value comparison. Pass (column, value) for equality or (column, operator, value) for an explicit supported operator. Equality/inequality with typed or untyped nil becomes IS NULL/IS NOT NULL. Invalid arguments record a sticky error. It mutates this draft and returns the same receiver. Invalid settings remain recorded until the published query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.Where("active", true)
})
_ = users
func (*Builder) WhereBetween ¶
WhereBetween appends an AND inclusive range comparison. It mutates this draft and returns the same receiver. Invalid settings remain recorded until the published query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.WhereBetween("score", 1, 10)
})
_ = users
func (*Builder) WhereColumn ¶
WhereColumn appends an AND comparison between columns. It mutates this draft and returns the same receiver. Invalid settings remain recorded until the published query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.WhereColumn("created_at", "<", "updated_at")
})
_ = users
func (*Builder) WhereExists ¶
WhereExists appends an AND EXISTS (sub) filter. The subquery must be a SELECT; correlate it with Builder.WhereColumn. MySQL rejects an UPDATE or DELETE whose subquery reads the target table (error 1093).
Example:
members := query.Table("members", func(statement *query.Builder) {
statement.WhereColumn("members.group_id", "posts.group_id")
statement.Where("members.user_id", 7)
})
_ = query.Table("posts", func(statement *query.Builder) {
statement.WhereExists(members)
})
func (*Builder) WhereGroup ¶
func (statement *Builder) WhereGroup(configure ...PredicateBuilderFunc) *Builder
WhereGroup appends a parenthesized AND predicate group. It mutates this draft and returns the same receiver. Invalid settings remain recorded until the published query is compiled. Callbacks run directly once, synchronously in order on one restricted draft; nil entries panic. The final group must contain predicates, even when callbacks are omitted or an expanded empty slice is supplied.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.WhereGroup(func(predicate *query.PredicateBuilder) {
predicate.Where("active", true)
})
})
_ = users
func (*Builder) WhereIn ¶
WhereIn appends an AND membership comparison. An empty value list, including an expanded nil slice, is always false and binds no arguments. Use an explicit type argument when no values infer T. It mutates this draft and returns the same receiver. Invalid settings remain recorded until the published query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.WhereIn("role", "admin", "editor")
})
_ = users
func (*Builder) WhereNotBetween ¶
WhereNotBetween appends an AND negative range comparison. It mutates this draft and returns the same receiver. Invalid settings remain recorded until the published query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.WhereNotBetween("score", 1, 10)
})
_ = users
func (*Builder) WhereNotExists ¶
WhereNotExists appends an AND NOT EXISTS (sub) filter. See Builder.WhereExists.
Example:
banned := query.Table("bans", func(statement *query.Builder) {
statement.WhereColumn("bans.user_id", "users.id")
})
_ = query.Table("users", func(statement *query.Builder) {
statement.WhereNotExists(banned)
})
func (*Builder) WhereNotGroup ¶
func (statement *Builder) WhereNotGroup(configure ...PredicateBuilderFunc) *Builder
WhereNotGroup appends a negated AND predicate group. It mutates this draft and returns the same receiver. Invalid settings remain recorded until the published query is compiled. Callbacks run directly once, synchronously in order on one restricted draft; nil entries panic. The final group must contain predicates, even when callbacks are omitted or an expanded empty slice is supplied.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.WhereNotGroup(func(predicate *query.PredicateBuilder) {
predicate.Where("active", true)
})
})
_ = users
func (*Builder) WhereNotIn ¶
WhereNotIn appends an AND negative membership comparison. An empty value list, including an expanded nil slice, is always true and binds no arguments; it does not restrict an UPDATE or DELETE to fewer rows. It mutates this draft and returns the same receiver. Invalid settings remain recorded until the published query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.WhereNotIn("role", "guest")
})
_ = users
func (*Builder) WhereNotNull ¶
WhereNotNull appends an AND not-null comparison. It mutates this draft and returns the same receiver. Invalid settings remain recorded until the published query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.WhereNotNull("email")
})
_ = users
func (*Builder) WhereNull ¶
WhereNull appends an AND null comparison. It mutates this draft and returns the same receiver. Invalid settings remain recorded until the published query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.WhereNull("deleted_at")
})
_ = users
func (*Builder) WhereRaw ¶
WhereRaw appends an AND filter rendered verbatim, such as "lower(email) = ?", with args bound to its placeholders. The placeholder rules of Builder.HavingRaw apply. Never pass user input as expression. It mutates this draft and returns the same receiver. Invalid settings remain recorded until the published query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.WhereRaw("lower(email) = ?", "ada@example.test")
})
_ = users
type BuilderFunc ¶
type BuilderFunc = func(*Builder)
BuilderFunc edits the shared construction draft used by Table, FromRaw, and Query.Derive. It is an alias, so ordinary callbacks and callback slices are accepted without conversion. Supplied callbacks run synchronously; publication detaches owned draft state, not arbitrary argument objects.
Example:
policies := []query.BuilderFunc{
func(statement *query.Builder) {
statement.Where("active", true)
},
}
users := query.Table("users", policies...)
_ = users
Example ¶
ExampleBuilderFunc composes a reusable construction policy through the public alias and compiles its published query, without retaining a mutable draft.
package main
import (
"fmt"
"arkoren.dev/foundation/query"
)
func main() {
policies := []query.BuilderFunc{
func(statement *query.Builder) {
statement.Where("active", true)
},
}
statement, err := query.Table("users", policies...).BuildFor(query.Postgres)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(statement.SQL())
fmt.Println(statement.Args())
}
Output: SELECT * FROM "users" WHERE "active" = $1 [true]
type Conflict ¶
type Conflict struct {
// contains filtered or unexported fields
}
Conflict selects how an INSERT handles rows that violate a unique constraint. Create one with Builder.OnConflict and finish it with Conflict.DoUpdate or Conflict.DoNothing. Completion changes the attached parent; publication detaches conflict state. Neither the child nor its parent draft can compile or execute SQL.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.Insert(map[string]any{"email": "ada@example.test", "name": "Ada"})
conflict := statement.OnConflict("email")
conflict.DoUpdate("name")
})
_ = users
func (*Conflict) DoNothing ¶
DoNothing skips rows that conflict. It renders ON CONFLICT DO NOTHING on Postgres and SQLite, and INSERT IGNORE on MySQL, which also downgrades other errors, such as invalid values, to warnings.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.Insert(map[string]any{"email": "ada@example.test", "name": "Ada"})
statement.OnConflict("email").DoNothing()
})
_ = users
func (*Conflict) DoUpdate ¶
DoUpdate overwrites the listed columns of the existing row with the values the INSERT proposed. It renders SET "c" = EXCLUDED."c" on Postgres and SQLite, and ON DUPLICATE KEY UPDATE `c` = VALUES(`c`) on MySQL. VALUES() is deprecated since MySQL 8.0.20 but works on every MySQL and MariaDB. The column list must be nonempty and contain no duplicate assignments. Invalid input records a sticky error on the parent draft.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.Insert(map[string]any{"email": "ada@example.test", "name": "Ada"})
statement.OnConflict("email").DoUpdate("name")
})
_ = users
type Dialect ¶
type Dialect struct {
// contains filtered or unexported fields
}
Dialect selects the SQL syntax a database expects for bind parameters and quoted identifiers. Dialects are opaque, comparable values; use ANSI, SQLite, Postgres, or MySQL. The zero Dialect is ANSI.
Example:
dialect := query.Postgres
statement, err := query.Table("users").BuildFor(dialect)
if err != nil {
return err
}
_ = statement.SQL()
func (Dialect) Quote ¶
Quote wraps one identifier part in the dialect's quote character and doubles any quote character inside it. It does not split qualified names and does not validate: quote "schema.table" part by part.
Example:
query.MySQL.Quote("users") // `users`
query.Postgres.Quote("users") // "users"
func (Dialect) String ¶
String returns the dialect name. Names are lowercase: ansi, sqlite, postgres, or mysql, including ansi for the zero Dialect. The result is diagnostic text, not a dialect constructor.
Example:
_ = query.Postgres.String() // "postgres"
func (Dialect) TransactionalDDL ¶
TransactionalDDL reports whether the dialect rolls back schema changes with the surrounding transaction. PostgreSQL and SQLite do. MySQL implicitly commits around most DDL statements. It is also false for ANSI: the standard does not require transactional DDL and real databases differ, so the compiler promises nothing for the generic dialect.
Example:
transactional := query.Postgres.TransactionalDDL() _ = transactional // true; schema execution can roll back with its transaction.
type Kind ¶
type Kind uint8
Kind identifies the SQL statement a Query compiles to. Its zero value is KindSelect; obtain the actual kind from Query.Inspect.
Example:
kind := query.Table("users").Inspect().Kind
_ = kind.String() // "SELECT"
const ( // KindSelect is the zero kind, so a new Builder selects. // // Example: // // _ = query.Table("users").Inspect().Kind == query.KindSelect KindSelect Kind = iota // KindInsert compiles rows into INSERT. // // Example: // // users := query.Table("users", func(statement *query.Builder) { // statement.Insert(map[string]any{"name": "Ada"}) // }) // _ = users.Inspect().Kind == query.KindInsert KindInsert // KindUpdate compiles updates and predicates into UPDATE. // // Example: // // users := query.Table("users", func(statement *query.Builder) { // statement.Update(map[string]any{"name": "Ada"}) // statement.Where("id", 7) // }) // _ = users.Inspect().Kind == query.KindUpdate KindUpdate // KindDelete compiles predicates into DELETE. // // Example: // // users := query.Table("users", func(statement *query.Builder) { // statement.Delete() // statement.Where("id", 7) // }) // _ = users.Inspect().Kind == query.KindDelete KindDelete )
type Order ¶
type Order struct {
// Column is the ordered column, qualified with dots when it was.
//
// Example:
//
// users := query.Table("users", func(statement *query.Builder) {
// statement.OrderBy("users.id")
// })
// _ = users.Inspect().Orders[0].Column // "users.id"
Column string
// Desc reports whether the ordering is descending.
//
// Example:
//
// users := query.Table("users", func(statement *query.Builder) {
// statement.OrderByDesc("id")
// })
// _ = users.Inspect().Orders[0].Desc // true
Desc bool
}
Order is one ordering of a Spec. Inspect returns orders in declaration order, with owned slice storage. Editing an Order changes only the inspection result, not its query.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.OrderByDesc("users.id")
})
ordering := users.Inspect().Orders[0]
_ = ordering.Column // "users.id"
_ = ordering.Desc // true
type PredicateBuilder ¶
type PredicateBuilder struct {
// contains filtered or unexported fields
}
PredicateBuilder declares only filters inside scopes and predicate groups. It has no source, selection, mutation, ordering, or compilation methods. Methods return this draft; publication detaches retained nested declarations.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.WhereGroup(func(predicate *query.PredicateBuilder) {
predicate.Where("active", true)
predicate.OrWhere("role", "admin")
})
})
_ = users
func (*PredicateBuilder) OrWhere ¶
func (predicate *PredicateBuilder) OrWhere(column string, args ...any) *PredicateBuilder
OrWhere adds the predicate described by Builder.OrWhere. It changes this restricted draft and returns the same receiver; validation errors remain sticky until the containing query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.WhereGroup(func(predicate *query.PredicateBuilder) {
predicate.OrWhere("role", "admin")
})
})
_ = users
func (*PredicateBuilder) OrWhereBetween ¶
func (predicate *PredicateBuilder) OrWhereBetween(column string, minimum, maximum any) *PredicateBuilder
OrWhereBetween adds the predicate described by Builder.OrWhereBetween. It changes this restricted draft and returns the same receiver; validation errors remain sticky until the containing query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.WhereGroup(func(predicate *query.PredicateBuilder) {
predicate.OrWhereBetween("score", 1, 10)
})
})
_ = users
func (*PredicateBuilder) OrWhereColumn ¶
func (predicate *PredicateBuilder) OrWhereColumn(first string, args ...string) *PredicateBuilder
OrWhereColumn adds the predicate described by Builder.OrWhereColumn. It changes this restricted draft and returns the same receiver; validation errors remain sticky until the containing query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.WhereGroup(func(predicate *query.PredicateBuilder) {
predicate.OrWhereColumn("created_at", "<", "updated_at")
})
})
_ = users
func (*PredicateBuilder) OrWhereExists ¶
func (predicate *PredicateBuilder) OrWhereExists(sub Query) *PredicateBuilder
OrWhereExists adds the predicate described by Builder.OrWhereExists. It changes this restricted draft and returns the same receiver; validation errors remain sticky until the containing query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.WhereGroup(func(predicate *query.PredicateBuilder) {
predicate.OrWhereExists(query.Table("members"))
})
})
_ = users
func (*PredicateBuilder) OrWhereGroup ¶
func (predicate *PredicateBuilder) OrWhereGroup(configure ...PredicateBuilderFunc) *PredicateBuilder
OrWhereGroup adds the predicate described by Builder.OrWhereGroup. It changes this restricted draft and returns the same receiver; validation errors remain sticky until the containing query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.WhereGroup(func(predicate *query.PredicateBuilder) {
predicate.OrWhereGroup(func(predicate *query.PredicateBuilder) {
predicate.Where("active", true)
})
})
})
_ = users
func (*PredicateBuilder) OrWhereIn ¶
func (predicate *PredicateBuilder) OrWhereIn[T any](column string, values ...T) *PredicateBuilder
OrWhereIn adds the predicate described by Builder.OrWhereIn. It changes this restricted draft and returns the same receiver; validation errors remain sticky until the containing query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.WhereGroup(func(predicate *query.PredicateBuilder) {
predicate.OrWhereIn("role", "admin", "editor")
})
})
_ = users
func (*PredicateBuilder) OrWhereNotBetween ¶
func (predicate *PredicateBuilder) OrWhereNotBetween(column string, minimum, maximum any) *PredicateBuilder
OrWhereNotBetween adds the predicate described by Builder.OrWhereNotBetween. It changes this restricted draft and returns the same receiver; validation errors remain sticky until the containing query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.WhereGroup(func(predicate *query.PredicateBuilder) {
predicate.OrWhereNotBetween("score", 1, 10)
})
})
_ = users
func (*PredicateBuilder) OrWhereNotExists ¶
func (predicate *PredicateBuilder) OrWhereNotExists(sub Query) *PredicateBuilder
OrWhereNotExists adds the predicate described by Builder.OrWhereNotExists. It changes this restricted draft and returns the same receiver; validation errors remain sticky until the containing query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.WhereGroup(func(predicate *query.PredicateBuilder) {
predicate.OrWhereNotExists(query.Table("bans"))
})
})
_ = users
func (*PredicateBuilder) OrWhereNotGroup ¶
func (predicate *PredicateBuilder) OrWhereNotGroup(configure ...PredicateBuilderFunc) *PredicateBuilder
OrWhereNotGroup adds the predicate described by Builder.OrWhereNotGroup. It changes this restricted draft and returns the same receiver; validation errors remain sticky until the containing query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.WhereGroup(func(predicate *query.PredicateBuilder) {
predicate.OrWhereNotGroup(func(predicate *query.PredicateBuilder) {
predicate.Where("active", true)
})
})
})
_ = users
func (*PredicateBuilder) OrWhereNotIn ¶
func (predicate *PredicateBuilder) OrWhereNotIn[T any](column string, values ...T) *PredicateBuilder
OrWhereNotIn adds the predicate described by Builder.OrWhereNotIn. It changes this restricted draft and returns the same receiver; validation errors remain sticky until the containing query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.WhereGroup(func(predicate *query.PredicateBuilder) {
predicate.OrWhereNotIn("role", "guest")
})
})
_ = users
func (*PredicateBuilder) OrWhereNotNull ¶
func (predicate *PredicateBuilder) OrWhereNotNull(column string) *PredicateBuilder
OrWhereNotNull adds the predicate described by Builder.OrWhereNotNull. It changes this restricted draft and returns the same receiver; validation errors remain sticky until the containing query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.WhereGroup(func(predicate *query.PredicateBuilder) {
predicate.OrWhereNotNull("email")
})
})
_ = users
func (*PredicateBuilder) OrWhereNull ¶
func (predicate *PredicateBuilder) OrWhereNull(column string) *PredicateBuilder
OrWhereNull adds the predicate described by Builder.OrWhereNull. It changes this restricted draft and returns the same receiver; validation errors remain sticky until the containing query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.WhereGroup(func(predicate *query.PredicateBuilder) {
predicate.OrWhereNull("deleted_at")
})
})
_ = users
func (*PredicateBuilder) OrWhereRaw ¶
func (predicate *PredicateBuilder) OrWhereRaw(expression string, args ...any) *PredicateBuilder
OrWhereRaw adds the predicate described by Builder.OrWhereRaw. It changes this restricted draft and returns the same receiver; validation errors remain sticky until the containing query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.WhereGroup(func(predicate *query.PredicateBuilder) {
predicate.OrWhereRaw("lower(email) = ?", "ada@example.test")
})
})
_ = users
func (*PredicateBuilder) Where ¶
func (predicate *PredicateBuilder) Where(column string, args ...any) *PredicateBuilder
Where adds the predicate described by Builder.Where. It changes this restricted draft and returns the same receiver; validation errors remain sticky until the containing query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.WhereGroup(func(predicate *query.PredicateBuilder) {
predicate.Where("active", true)
})
})
_ = users
func (*PredicateBuilder) WhereBetween ¶
func (predicate *PredicateBuilder) WhereBetween(column string, minimum, maximum any) *PredicateBuilder
WhereBetween adds the predicate described by Builder.WhereBetween. It changes this restricted draft and returns the same receiver; validation errors remain sticky until the containing query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.WhereGroup(func(predicate *query.PredicateBuilder) {
predicate.WhereBetween("score", 1, 10)
})
})
_ = users
func (*PredicateBuilder) WhereColumn ¶
func (predicate *PredicateBuilder) WhereColumn(first string, args ...string) *PredicateBuilder
WhereColumn adds the predicate described by Builder.WhereColumn. It changes this restricted draft and returns the same receiver; validation errors remain sticky until the containing query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.WhereGroup(func(predicate *query.PredicateBuilder) {
predicate.WhereColumn("created_at", "<", "updated_at")
})
})
_ = users
func (*PredicateBuilder) WhereExists ¶
func (predicate *PredicateBuilder) WhereExists(sub Query) *PredicateBuilder
WhereExists adds the predicate described by Builder.WhereExists. It changes this restricted draft and returns the same receiver; validation errors remain sticky until the containing query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.WhereGroup(func(predicate *query.PredicateBuilder) {
predicate.WhereExists(query.Table("members"))
})
})
_ = users
func (*PredicateBuilder) WhereGroup ¶
func (predicate *PredicateBuilder) WhereGroup(configure ...PredicateBuilderFunc) *PredicateBuilder
WhereGroup adds the predicate described by Builder.WhereGroup. It changes this restricted draft and returns the same receiver; validation errors remain sticky until the containing query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.WhereGroup(func(predicate *query.PredicateBuilder) {
predicate.WhereGroup(func(predicate *query.PredicateBuilder) {
predicate.Where("active", true)
})
})
})
_ = users
func (*PredicateBuilder) WhereIn ¶
func (predicate *PredicateBuilder) WhereIn[T any](column string, values ...T) *PredicateBuilder
WhereIn adds the predicate described by Builder.WhereIn. It changes this restricted draft and returns the same receiver; validation errors remain sticky until the containing query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.WhereGroup(func(predicate *query.PredicateBuilder) {
predicate.WhereIn("role", "admin", "editor")
})
})
_ = users
func (*PredicateBuilder) WhereNotBetween ¶
func (predicate *PredicateBuilder) WhereNotBetween(column string, minimum, maximum any) *PredicateBuilder
WhereNotBetween adds the predicate described by Builder.WhereNotBetween. It changes this restricted draft and returns the same receiver; validation errors remain sticky until the containing query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.WhereGroup(func(predicate *query.PredicateBuilder) {
predicate.WhereNotBetween("score", 1, 10)
})
})
_ = users
func (*PredicateBuilder) WhereNotExists ¶
func (predicate *PredicateBuilder) WhereNotExists(sub Query) *PredicateBuilder
WhereNotExists adds the predicate described by Builder.WhereNotExists. It changes this restricted draft and returns the same receiver; validation errors remain sticky until the containing query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.WhereGroup(func(predicate *query.PredicateBuilder) {
predicate.WhereNotExists(query.Table("bans"))
})
})
_ = users
func (*PredicateBuilder) WhereNotGroup ¶
func (predicate *PredicateBuilder) WhereNotGroup(configure ...PredicateBuilderFunc) *PredicateBuilder
WhereNotGroup adds the predicate described by Builder.WhereNotGroup. It changes this restricted draft and returns the same receiver; validation errors remain sticky until the containing query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.WhereGroup(func(predicate *query.PredicateBuilder) {
predicate.WhereNotGroup(func(predicate *query.PredicateBuilder) {
predicate.Where("active", true)
})
})
})
_ = users
func (*PredicateBuilder) WhereNotIn ¶
func (predicate *PredicateBuilder) WhereNotIn[T any](column string, values ...T) *PredicateBuilder
WhereNotIn adds the predicate described by Builder.WhereNotIn. It changes this restricted draft and returns the same receiver; validation errors remain sticky until the containing query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.WhereGroup(func(predicate *query.PredicateBuilder) {
predicate.WhereNotIn("role", "guest")
})
})
_ = users
func (*PredicateBuilder) WhereNotNull ¶
func (predicate *PredicateBuilder) WhereNotNull(column string) *PredicateBuilder
WhereNotNull adds the predicate described by Builder.WhereNotNull. It changes this restricted draft and returns the same receiver; validation errors remain sticky until the containing query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.WhereGroup(func(predicate *query.PredicateBuilder) {
predicate.WhereNotNull("email")
})
})
_ = users
func (*PredicateBuilder) WhereNull ¶
func (predicate *PredicateBuilder) WhereNull(column string) *PredicateBuilder
WhereNull adds the predicate described by Builder.WhereNull. It changes this restricted draft and returns the same receiver; validation errors remain sticky until the containing query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.WhereGroup(func(predicate *query.PredicateBuilder) {
predicate.WhereNull("deleted_at")
})
})
_ = users
func (*PredicateBuilder) WhereRaw ¶
func (predicate *PredicateBuilder) WhereRaw(expression string, args ...any) *PredicateBuilder
WhereRaw adds the predicate described by Builder.WhereRaw. It changes this restricted draft and returns the same receiver; validation errors remain sticky until the containing query is compiled.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.WhereGroup(func(predicate *query.PredicateBuilder) {
predicate.WhereRaw("lower(email) = ?", "ada@example.test")
})
})
_ = users
type PredicateBuilderFunc ¶
type PredicateBuilderFunc = func(*PredicateBuilder)
PredicateBuilderFunc edits only predicates in a group or scope. Group callbacks share one restricted draft; each Scope callback has its own. The alias preserves identity with func(*PredicateBuilder), including slices.
Example:
policies := []query.PredicateBuilderFunc{
func(predicate *query.PredicateBuilder) {
predicate.Where("active", true)
},
}
users := query.Table("users", func(statement *query.Builder) {
statement.Scope(policies...)
})
_ = users
Example ¶
ExamplePredicateBuilderFunc uses the restricted callback alias for a protected scope, leaving the whole user disjunction inside its own AND boundary.
package main
import (
"fmt"
"arkoren.dev/foundation/query"
)
func main() {
policies := []query.PredicateBuilderFunc{
func(predicate *query.PredicateBuilder) {
predicate.Where("active", true)
},
}
statement, err := query.Table("users", func(statement *query.Builder) {
statement.Where("role", "admin")
statement.OrWhere("role", "owner")
statement.Scope(policies...)
}).BuildFor(query.Postgres)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(statement.SQL())
fmt.Println(statement.Args())
}
Output: SELECT * FROM "users" WHERE ("role" = $1 OR "role" = $2) AND ("active" = $3) [admin owner true]
type Query ¶
type Query struct {
// contains filtered or unexported fields
}
Query is an immutable SQL definition, compiled lazily for a chosen dialect. Construct it with Table or FromRaw and use Derive for independent variants. Its owned containers are detached from construction drafts. Arbitrary argument objects remain caller-owned: do not mutate them while compiling or executing.
Example:
users := query.Table("users")
statement, err := users.BuildFor(query.Postgres)
if err != nil {
return err
}
_ = statement.SQL()
func FromRaw ¶
func FromRaw(statement RawStatement, configure ...BuilderFunc) Query
FromRaw publishes a SELECT over a raw subquery, with default alias "t". Use As to change the alias. Positional or named source arguments bind before outer predicates through the normal dialect compiler, not precompiled SQL. Empty SQL and source mutations are invalid. Callback invocation and ownership follow Table; trusted SQL is required because source text is not escaped.
Example:
users := query.FromRaw(query.Raw("SELECT id FROM users WHERE active = ?", true),
func(statement *query.Builder) {
statement.Where("id", ">", 10)
})
_ = users
func Table ¶
func Table(name string, configure ...BuilderFunc) Query
Table publishes a query targeting name after invoking configure in order. Callbacks share one draft and run synchronously once; supplied nil callbacks panic. Invalid names return an invalid query before callbacks are invoked. No callbacks, including an expanded empty slice, selects every column.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.Select("id", "name")
})
_ = users
func (Query) Build ¶
Build compiles the query into parameterized SQL for the ANSI dialect.
Example:
statement, err := query.Table("users").Build()
if err != nil {
return err
}
_ = statement.SQL()
func (Query) BuildFor ¶
BuildFor compiles the query into parameterized SQL for dialect. Raw expressions passed to *Raw methods use ? placeholders; BuildFor rewrites each ? outside quoted text and comments to the dialect placeholder, and ?? stands for a literal question mark. INSERT, UPDATE, and DELETE builders that carry clauses they cannot use, such as joins or limits, fail. SQLite rejects qualified INSERT, UPDATE, and conflict column names with ErrUnsupported; qualified table names and read-side references remain valid.
Example:
statement, err := query.Table("users").BuildFor(query.Postgres)
if err != nil {
return err
}
_ = statement.SQL()
func (Query) Count ¶
Count returns a SELECT COUNT(*) query for the rows the query selects, ignoring ordering, limit, offset, and row locks, so it is the companion of a paged query. A plain query keeps its table, joins, and filters and swaps the select list for COUNT(*). A grouped, DISTINCT, HAVING, or raw-sourced query is wrapped, as in SELECT COUNT(*) FROM (SELECT ...) AS "count", so the count matches the rows the original returns; its select list must then have unique column names. Only SELECT queries can be counted.
Example:
users := query.Table("users")
total := users.Count()
_ = total
func (Query) Derive ¶
func (query Query) Derive(configure ...BuilderFunc) Query
Derive publishes an independent variant after applying configure to one draft. Callbacks run directly once, synchronously and in order; nil callbacks panic. No callbacks preserves the base, including its first error. Settings cannot repair inherited errors. Retained drafts cannot change either published value.
Example:
base := query.Table("users")
active := base.Derive(func(statement *query.Builder) {
statement.Where("active", true)
})
_ = active
func (Query) Inspect ¶
Inspect returns a snapshot of the query's structure. Slices and pointers in the result are copies, so the query stays immutable.
Example:
users := query.Table("users")
_ = users.Inspect().Table
func (Query) IsSelect ¶
IsSelect reports whether the query compiles to a SELECT statement, as opposed to an INSERT, UPDATE, or DELETE.
Example:
users := query.Table("users")
_ = users.IsSelect()
func (Query) Seek ¶
Seek adds a keyset pagination filter for the rows after (or, with backward, before) the row whose ordering values are values. It reads the query's orders, so call it after OrderBy and friends, and pass one value per ordering in the same sequence. For orders (a ASC, b DESC) it appends
WHERE (a > ? OR (a = ? AND b < ?))
to the existing filters, which are parenthesized when they contain OR. Backward reverses every ordering direction and comparison, returning rows in reverse order for paging toward the start; reverse the fetched rows to restore display order. Call Seek once per query. Like a scope, the filter is ANDed after the existing filters even when they contain OR. Values must not be nil, because NULL never compares; order by a non-null unique column last so rows have a total order. Only SELECT accepts it.
Example:
users := query.Table("users", func(statement *query.Builder) {
statement.OrderBy("id")
})
next := users.Seek([]any{7}, false)
_ = next
type RawStatement ¶
type RawStatement struct {
// contains filtered or unexported fields
}
RawStatement is caller-written SQL with positional ? or named :name placeholders, constructed by Raw or RawNamed. Compilation checks nonblank text and bindings, not SQL syntax. Use it for statements the structured API cannot express. Never build SQL from user input; pass values as arguments. Owned argument containers are copied; objects in their slots remain shared.
Example:
definition := query.Raw("SELECT id FROM users WHERE active = ?", true)
statement, err := definition.BuildFor(query.Postgres)
if err != nil {
return err
}
_ = statement.SQL()
Example ¶
ExampleRawStatement compiles named bindings and demonstrates that Args owns its returned slice independently of both the raw definition and statement.
package main
import (
"fmt"
"arkoren.dev/foundation/query"
)
func main() {
definition := query.RawNamed("SELECT :id", map[string]any{"id": 7})
statement, err := definition.BuildFor(query.Postgres)
if err != nil {
fmt.Println(err)
return
}
arguments := statement.Args()
arguments[0] = 9
fmt.Println(statement.SQL(), statement.Args())
fmt.Println(query.Postgres.String(), query.Postgres.TransactionalDDL())
}
Output: SELECT $1 [7] postgres true
func Raw ¶
func Raw(sql string, args ...any) RawStatement
Raw creates a RawStatement from sql and its arguments. Placeholders follow the raw expression rules of Builder.HavingRaw: ? outside quotes and comments becomes the dialect placeholder, and ?? writes a literal ?. The argument slice is copied; argument objects themselves remain shared.
Example:
statement, err := query.Raw("SELECT ?", 7).BuildFor(query.Postgres)
if err != nil {
return err
}
_ = statement.SQL() // "SELECT $1"
func RawNamed ¶
func RawNamed(sql string, args map[string]any) RawStatement
RawNamed creates a RawStatement whose placeholders are names: each :name outside quotes and comments becomes the dialect placeholder and binds args["name"]. A name is a letter or underscore followed by letters, digits, and underscores, and may repeat, binding its value again each time. "::" is kept as written, so PostgreSQL casts such as id::text work. A ? is not a placeholder here and is kept as written. Quotes and comments follow the rules of Raw. A name without an entry in args fails at Build with ErrInvalidQuery; entries that no name uses are ignored, so the map returned by Values can be passed whole. The map is copied.
Example:
statement := query.RawNamed("UPDATE users SET name = :name WHERE id = :id",
map[string]any{"name": "Ada", "id": 7})
func (RawStatement) Build ¶
func (statement RawStatement) Build() (Statement, error)
Build compiles the statement for the ANSI dialect. Binding failures have the same ErrInvalidQuery classification as BuildFor. It does not validate the SQL syntax or execute the statement.
Example:
statement, err := query.Raw("SELECT ?", 7).Build()
if err != nil {
return err
}
_ = statement.Args()
func (RawStatement) BuildFor ¶
func (statement RawStatement) BuildFor(dialect Dialect) (Statement, error)
BuildFor rewrites the placeholders for dialect. It fails with ErrInvalidQuery when the SQL is blank, the positional placeholder count differs from its argument count, or a named placeholder has no argument. Unused entries in a named argument map are ignored, as described by RawNamed. It leaves the definition unchanged and does not validate SQL syntax.
Example:
definition := query.RawNamed("SELECT :id", map[string]any{"id": 7})
statement, err := definition.BuildFor(query.Postgres)
if err != nil {
return err
}
_ = statement.SQL() // "SELECT $1"
type Scope ¶
type Scope = PredicateBuilderFunc
Scope is a reusable function that adds predicates to a query.
Example:
active := func(predicate *query.PredicateBuilder) {
predicate.Where("active", true)
}
_ = query.Table("users", func(statement *query.Builder) {
statement.Scope(active)
})
type Spec ¶
type Spec struct {
// Kind is the statement the query compiles to.
//
// Example:
//
// _ = query.Table("users").Inspect().Kind == query.KindSelect
Kind Kind
// Table is the source table as written, such as "public.users". It is
// empty when the source is raw SQL.
//
// Example:
//
// _ = query.Table("public.users").Inspect().Table // "public.users"
Table string
// Orders lists the orderings in the order they apply; nil when none.
//
// Example:
//
// users := query.Table("users", func(statement *query.Builder) {
// statement.OrderBy("id")
// })
// _ = users.Inspect().Orders[0].Column
Orders []Order
// Limit is a copy of the row limit; nil when unset.
// Explicit zero requests no rows and differs from an unset limit.
//
// Example:
//
// spec := query.Table("users", func(statement *query.Builder) {
// statement.Limit(20)
// }).Inspect()
// if spec.Limit != nil {
// _ = *spec.Limit // 20
// }
Limit *int
// Offset is a copy of the row offset; nil when unset.
// Explicit zero remains present even though it skips no rows.
//
// Example:
//
// spec := query.Table("users", func(statement *query.Builder) {
// statement.Offset(0)
// }).Inspect()
// if spec.Offset != nil {
// _ = *spec.Offset // 0
// }
Offset *int
// Distinct reports whether the select is DISTINCT.
//
// Example:
//
// users := query.Table("users", func(statement *query.Builder) {
// statement.Distinct()
// })
// _ = users.Inspect().Distinct // true
Distinct bool
// Grouped reports whether the query has GROUP BY columns.
//
// Example:
//
// users := query.Table("users", func(statement *query.Builder) {
// statement.Select("role")
// statement.GroupBy("role")
// })
// _ = users.Inspect().Grouped // true
Grouped bool
// Having reports whether the query has HAVING filters.
//
// Example:
//
// users := query.Table("users", func(statement *query.Builder) {
// statement.SelectRaw("COUNT(*)")
// statement.HavingRaw("COUNT(*) > ?", 5)
// })
// _ = users.Inspect().Having // true
Having bool
// Raw reports whether the source is a [FromRaw] subquery.
// Query-sourced wrappers created by Count do not set this flag.
//
// Example:
//
// users := query.FromRaw(query.Raw("SELECT id FROM users"))
// _ = users.Inspect().Raw // true
Raw bool
// Returning reports whether an INSERT, UPDATE, or DELETE has a
// RETURNING clause and so yields rows.
//
// Example:
//
// users := query.Table("users", func(statement *query.Builder) {
// statement.Insert(map[string]any{"name": "Ada"})
// statement.Returning("id")
// })
// _ = users.Inspect().Returning // true; compile for PostgreSQL or SQLite.
Returning bool
}
Spec is a read-only snapshot of a Query, for callers such as pagination helpers that need to know what a query selects without compiling it. Changing a Spec never affects the query it came from. Inspection reports declarations even if later compilation rejects them; it does not validate the query or resolve a database schema.
Example:
spec := query.Table("users", func(statement *query.Builder) {
statement.OrderBy("id")
statement.Limit(20)
}).Inspect()
_ = spec.Table
_ = spec.Orders
Example ¶
ExampleSpec inspects paging and ordering without compiling SQL, then shows that changing the snapshot's pointer does not change the published query.
package main
import (
"fmt"
"arkoren.dev/foundation/query"
)
func main() {
users := query.Table("users", func(statement *query.Builder) {
statement.OrderByDesc("users.id")
statement.Limit(20)
})
spec := users.Inspect()
fmt.Println(spec.Kind.String(), spec.Table, spec.Orders[0].Column, spec.Orders[0].Desc)
*spec.Limit = 0
fmt.Println(*users.Inspect().Limit)
}
Output: SELECT users users.id true 20
type Statement ¶
type Statement struct {
// contains filtered or unexported fields
}
Statement is compiled SQL and its ordered positional arguments. Obtain it through Query or RawStatement compilation. SQL returns immutable text; Args copies its outer slice, not the objects stored in argument slots. The zero Statement contains empty SQL and no arguments.
Example:
statement, err := query.Raw("SELECT ?", 7).BuildFor(query.Postgres)
if err != nil {
return err
}
_ = statement.SQL() // "SELECT $1"
_ = statement.Args() // []any{7}
func (Statement) Args ¶
Args returns a copy of the ordered positional arguments. Editing the returned slice cannot change the statement. Argument objects themselves remain shared; no arguments returns nil.
Example:
statement, err := query.Raw("SELECT ?", 7).Build()
if err != nil {
return err
}
arguments := statement.Args()
arguments[0] = 9 // A later statement.Args() still contains 7.
func (Statement) SQL ¶
SQL returns the compiled SQL text. It contains placeholders rather than interpolated argument values; pass Args separately to the database. The zero Statement returns an empty string.
Example:
statement, err := query.Table("users").Build()
if err != nil {
return err
}
_ = statement.SQL() // SELECT * FROM "users"
type Statementer ¶
type Statementer interface {
// BuildFor compiles a published definition for dialect, returning SQL and
// ordered arguments or the definition's validation or dialect error.
//
// Example:
//
// var definition query.Statementer = query.Table("users")
// statement, err := definition.BuildFor(query.Postgres)
// if err != nil {
// return err
// }
// _ = statement.Args()
BuildFor(dialect Dialect) (Statement, error)
}
Statementer is implemented by everything that compiles to exactly one Statement: the DML Query, RawStatement, and each statement of a schema operation (arkoren.dev/foundation/query/schema.Operation). Mutable declaration drafts do not implement it; execution boundaries accept completed definitions and compile them for the database's actual dialect.
Example:
var definition query.Statementer = query.Table("users")
statement, err := definition.BuildFor(query.Postgres)
if err != nil {
return err
}
_ = statement.SQL()
type ValuesOption ¶
type ValuesOption func(*valuesConfig)
ValuesOption filters columns for one Values call. Obtain options from Only and Except; reusable options retain copied names, not struct field values. A supplied nil option panics when Values invokes it.
Example:
options := []query.ValuesOption{query.Only("name"), query.Except("id")}
values := query.Values(struct{ Name string }{Name: "Ada"}, options...)
_ = values["name"]
func Except ¶
func Except(columns ...string) ValuesOption
Except drops the named columns. Names that are not columns of the struct are ignored. Several Except options add up, and Except applies after Only. With no columns, including an expanded nil or empty slice, it drops nothing.
Except copies the column-name slice when the option is created. Later changes to the caller's slice do not affect the option, which can be reused.
Example:
user := struct {
ID int
Name string
}{ID: 7, Name: "Ada"}
values := query.Values(user, query.Except("id"))
_ = values["name"] // "Ada"; "id" is absent.
func Only ¶
func Only(columns ...string) ValuesOption
Only keeps just the named columns. Names that are not columns of the struct are ignored. Several Only options add up.
With no columns, including an expanded nil or empty slice, Only adds no restriction: by itself it keeps every column and does not clear other Only options. Except still applies after Only.
Only copies the column-name slice when the option is created. Later changes to the caller's slice do not affect the option, which can be reused.
Example:
user := struct {
ID int
Name string
}{ID: 7, Name: "Ada"}
values := query.Values(user, query.Only("name"))
_ = values["name"] // "Ada"; "id" is absent.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package schema builds DDL: creating, altering, renaming, and dropping tables, with their columns, keys, indexes, and foreign keys, for the dialects of arkoren.dev/foundation/query.
|
Package schema builds DDL: creating, altering, renaming, and dropping tables, with their columns, keys, indexes, and foreign keys, for the dialects of arkoren.dev/foundation/query. |