sqlb

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 11, 2026 License: Apache-2.0 Imports: 10 Imported by: 0

README

sqlb — typed SQL for Go

A typed SQL query builder for Go libraries that own tables and cannot carry an ORM. Zero dependencies.

License Go Dependencies Coverage Conformance

sqlb makes a database column a typed Go value. A library exports its columns; an application composes them into queries, and the compiler checks every column, comparison, join condition and scan target. It speaks database/sql directly — pgx, modernc SQLite, clickhouse-go, anything with a driver — and ships engine packages for Postgres, SQLite, MySQL and ClickHouse, plus extension sub-packages for pgvector, pg_trgm, ltree, pgcrypto, earthdistance, unaccent and PostGIS.

go get github.com/ubgo/sqlb                        # the builder, sqlbtest, the engine packages — zero dependencies
go get github.com/ubgo/sqlb/contrib/driver-sqlite  # SQLite, with the pragmas a library-owned schema needs
go get github.com/ubgo/sqlb/contrib/driver-pgx     # Postgres, through pgx's database/sql adapter
go get github.com/ubgo/sqlb/contrib/driver-mysql   # MySQL 8, through go-sql-driver/mysql

Every module is tagged together: the core as v0.1.0, each contrib module as contrib/<name>/v0.1.0. Inside the repository a committed go.work resolves the modules against each other.

Why it exists

A library that ships its own tables has a problem no ORM solves for it. It must let the application join across the boundary into tables it has never heard of, and it must do so without pulling a code generator or a second ORM into an application that may already have one. Passing SQL strings across that boundary gives the application no type safety and the library no control. sqlb is the smallest thing that gives both: the library exports typed column values, the application composes them, and the compiler checks the join. docs/cross-package.md is that story end to end — four packages that do not import each other, consolidated into one statement.

  • A column is an Expr[T]. Comparing it with the wrong type, joining it to a mismatched column, or binding it to the wrong pointer is a compile error, not a runtime surprise.
  • A query carries its own scan targets. The column list and the destinations come from one slice, so inserting a column in the middle cannot silently shift a scan.
  • Zero third-party dependencies in the core, enforced by a gate rather than by good intentions. Embedding sqlb adds no driver, no cgo and no version conflict.
  • Engine packages carry what one engine spells differently. JSONB, arrays, ranges, full-text search, DISTINCT ON, ON CONFLICT, MERGE and RETURNING in pg; JSON1, FTS5 and both upsert forms in sqlite; PREWHERE, FINAL and LIMIT BY in ch. The pg surface is the large one — about five hundred constructs to the core's two hundred and sixty, which is what the universality rule produces: almost nothing is spelled identically by four engines.
  • Extensions are sub-packages over exported guarded constructors, so adding pgvector support, or the next extension nobody has written yet, never edits the engine package.

Checked three times, not claimed once

"Type-safe" means different things in different places, so here is exactly what is checked, where, and by what.

1. The compiler checks the typed core. A string column bound to an *int, a time column compared with a string, a vector distance over a text column, a sum over a non-numeric column — none of these compile. That is not asserted in prose: examples/typecheck holds deliberately wrong statements behind a build tag, and the gate compiles it and fails if it ever succeeds. An engine clause is also typed to its dialect, so pg.ForUpdate() on a SQLite statement does not compile either.

2. Build checks engine expressions. A pg, sqlite or ch expression records its own name while rendering, and under another dialect Build returns ErrUnsupportedDialect naming the construct — in your process, before anything reaches a database:

dist := vector.L2(embedding, vector.ParamVector(query))

_, err := sqlb.NewSelect(sqlb.SQLiteDialect{}).Columns(sqlb.Bind(dist, &d)).From(docs).Build()
// err: sqlb: construct is not supported by this dialect
//      vector.L2 under dialect "sqlite"

This is the honest limit of the type system: engine expressions are checked when the statement is built, not when the program is compiled. The failure is still in your process and still names the construct — it is never a syntax error from the database at three in the morning.

3. A real database checks execution. Rendering SQL and running it are different claims, and only execution proves the second. sqlbtest is a shared conformance suite every driver runs; contrib/driver-sqlite executes every sqlite construct against a real database, and contrib/driver-pgx executes every construct in pg and its eight extension packages — 593 cases — against Postgres 17, and the same suite again against Postgres 18 for the constructs 17 refuses. Those runs found eleven constructs whose rendering tests passed and whose SQL Postgres rejects, which is why execution, not rendering, is the bar for calling a construct proven. ch is the proof that the extension points carry a second engine; it has not been executed against a server.

How it compares

Columns are categories, not products, and every cell is a claim about this repository or about what a category does by definition.

sqlb ORM or code generator String-building query builder Hand-written SQL
Columns, joins and scan targets checked by the compiler ✅ ✅ ❌ ❌
Works with no code-generation step ✅ ❌ ✅ ✅
Third-party dependencies to adopt it none ⚠️ varies ⚠️ varies none
A library can export typed columns for code that has never seen its schema ✅ ⚠️ only if both sides adopt the same tool ❌ ❌
An engine-only construct fails before reaching the database ✅ ⚠️ varies ❌ ❌
Driver proven by a shared conformance suite ✅ ⚠️ varies ❌ ❌
Migrations, relation graph, generated models ❌ ✅ ❌ ❌

If you want migrations, a relation graph and generated models, an ORM is the right tool. If you want a library's tables to be joinable by code that has never seen its schema, without either side adopting the other's ORM, that is sqlb.

What it is not

It is not an ORM. There is no relation graph, no code generation, no derived DDL and no lazy loading. A project that wants those should use an ORM; this exists for libraries that cannot.

Documentation

Every command this repository uses, from the gate to running the proof on all three engines, is in COMMANDS.md.

Start at docs/README.md. It indexes one document per topic — the core builder, dialects, the engine packages, extensions, execution, the conformance suite — and one page per package with runnable snippets, every one of them copied from a test whose expected output go test checks. docs/examples.md indexes the compile-checked catalogues.

Get started

Ten minutes end to end: declare a table, open a database, read a row, join across a package boundary, write, and reach for the one construct only your engine has.

1. Declare your tables

A library declares its tables once, as package-level values. Everything downstream — the library's own queries, the application's queries, the compiler's checking — works from these. There is no generator and nothing to re-run when a column is added.

package users

import (
	"time"

	"github.com/ubgo/sqlb"
)

var Table = sqlb.Table{Name: "app_user", Alias: "u"}

var (
	ID        = sqlb.Col[string](Table, "id")
	Email     = sqlb.Col[string](Table, "email")
	CreatedAt = sqlb.Col[time.Time](Table, "created_at")
)

Col[T] is where the type comes from. Email is an sqlb.Expr[string], so comparing it to an int, binding it to an *int or joining it to a time.Time column is a compile error.

2. Open a database

sqlb never opens a connection for you — it takes any database/sql handle, so *sql.DB and *sql.Tx both work and your existing pool is the pool. The two contrib modules are a convenience that applies the settings each engine wants, nothing more.

Postgres, through pgx's database/sql adapter:

import driverpgx "github.com/ubgo/sqlb/contrib/driver-pgx"

db, err := driverpgx.Open("postgres://user:pass@localhost:5432/app?sslmode=disable")
if err != nil {
	return err
}
defer db.Close()

Open sets a pool size and a connection lifetime and pings, so a bad DSN fails there rather than at your first query. Using pgx directly — sql.Open("pgx", dsn) — works identically; the module exists so those defaults are not retyped.

SQLite, through the pure-Go modernc driver:

import driversqlite "github.com/ubgo/sqlb/contrib/driver-sqlite"

db, err := driversqlite.Open("app.db")   // or driversqlite.Memory() in tests
if err != nil {
	return err
}
defer db.Close()

Open applies the pragmas a library-owned schema needs — WAL, foreign keys, a busy timeout — to every connection in the pool, which is the part that is easy to get wrong once and then debug for a week. Memory() pins the pool to a single connection, because an in-memory SQLite database belongs to the connection that created it and a pool of two serves the second query from a second, empty database.

MySQL 8, through go-sql-driver/mysql:

import drivermysql "github.com/ubgo/sqlb/contrib/driver-mysql"

db, err := drivermysql.Open("user:pass@tcp(127.0.0.1:3306)/app")
if err != nil {
	return err
}
defer db.Close()

Open forces parseTime and UTC whatever the DSN says, because the MySQL dialect writes times in UTC and a DATETIME read back in another zone shifts silently.

ClickHouse and anything else work the same way: bring your own driver and pass the *sql.DB.

3. Fix the dialect once

Every statement carries its dialect in its type. Write it out —

q, err := sqlb.NewSelect(sqlb.PostgresDialect{}). /* … */ Build()

— or start it from your engine's package and stop repeating it. pg.Select is Postgres, sqlite.Select is SQLite and mysql.Select is MySQL, and the same package holds every construct you put inside the statement:

import "github.com/ubgo/sqlb/pg"

q, err := pg.Select().Columns(sqlb.Bind(users.Email, &email)).
	From(users.Table).
	Where(sqlb.Eq(users.ID, "usr_1")).
	Build()

Select() with no arguments is the long form exactly, for a statement whose select items are bindings added with Columns — which is what a query you intend to scan wants. Passing items to Select directly adds them without scan targets, for a subquery or a CTE body.

The dialect is not decoration. pg.ForUpdate() on a SQLite statement does not compile, and a pg expression under the SQLite dialect fails at Build naming the construct.

4. Read a row, then many

Bind each column to a destination. One scans through the bindings, so nothing is passed for the destination at the call site — the column list and the scan targets come from one slice and cannot drift apart when someone inserts a column in the middle.

var id, email string

q, err := pg.Select().Columns(sqlb.Bind(users.ID, &id), sqlb.Bind(users.Email, &email)).
	From(users.Table).
	Where(sqlb.Eq(users.Email, "a@example.com")).
	Build()
if err != nil {
	return err
}

if err := sqlb.One(ctx, db, q); err != nil {
	return err   // sql.ErrNoRows when there is no such user
}

Each refills the same destinations per row and calls your function:

err = sqlb.Each(ctx, db, q, func() error {
	fmt.Println(id, email)   // the current row
	return nil
})

A Query holds pointers into id and email, so it is not safe to share between goroutines — build it where its destinations live. One and Each refuse an overlapping execution with ErrQueryInUse rather than returning interleaved rows. When you want a query you can share, the scan package's readers allocate per row instead of writing through your pointers.

5. Join across a package boundary

This is the boundary the library exists for. Another package declares invoices exactly as above, and a third package joins the two without either side having seen the other's schema:

var email string
var total int64

q, err := pg.Select().Columns(sqlb.Bind(users.Email, &email), sqlb.Bind(invoices.Total, &total)).
	From(users.Table).
	Join(sqlb.Join{
		Type:  sqlb.JoinInner,
		Table: invoices.Table,
		On:    []sqlb.Predicate{sqlb.EqExpr(users.ID, invoices.UserID)},
	}).
	Order(sqlb.OrderBy(invoices.Total, sqlb.SortDesc)).
	Limit(10).
	Build()

// SELECT "u"."email", "i"."total" FROM "app_user" AS "u"
//   INNER JOIN "invoice" AS "i" ON ("u"."id" = "i"."user_id")
//   ORDER BY "i"."total" DESC LIMIT $1

EqExpr compares two columns, and it compares them at their Go types: joining users.ID to a time.Time column does not compile.

6. Write

Inserts, updates and deletes end in Build() the same way. An UPDATE or DELETE without a WHERE does not compile — the unfiltered statement has no Build method at all, so say All() when you mean every row. RETURNING is a function rather than a method because it is conditional on the dialect having it; on ClickHouse the line does not compile.

var newID string

ins, err := pg.InsertReturning(
	pg.Insert(users.Table).
		Row(sqlb.Set(users.ID, "usr_1"), sqlb.Set(users.Email, "a@example.com")),
	sqlb.Bind(users.ID, &newID),
).Build()

upd, err := pg.Update(users.Table).
	Set(sqlb.Set(users.Email, "b@example.com")).
	Where(sqlb.Eq(users.ID, "usr_1")).
	Build()

del, err := pg.Delete(users.Table).
	Where(sqlb.Eq(users.ID, "usr_1")).
	Build()
7. Use what only your engine has

The core carries only what Postgres, MySQL, SQLite and ClickHouse all spell identically. Everything else lives in an engine package — pg, sqlite, mysql, ch — and composes with the core like any other expression or clause.

Postgres: an upsert, and a full-text search over user input.

import "github.com/ubgo/sqlb/pg"

up, err := pg.Insert(docs.Table).
	Row(sqlb.Set(docs.ID, "d1"), sqlb.Set(docs.Title, "Ada")).
	Clause(pg.OnConflict(docs.ID).
		DoUpdate(sqlb.SetExpr(docs.Title, pg.Excluded(docs.Title))).
		Clause()).
	Build()

hits, err := pg.Select().Columns(sqlb.Bind(docs.ID, &id)).
	From(docs.Table).
	Where(pg.Matches(
		pg.ToTSVector("english", docs.Body),
		pg.WebsearchToTSQuery("english", userQuery),
	)).
	Build()

SQLite: the same upsert in SQLite's spelling, and FTS5.

import "github.com/ubgo/sqlb/sqlite"

up, err := sqlite.Insert(docs.Table).
	Row(sqlb.Set(docs.ID, "d1"), sqlb.Set(docs.Title, "Ada")).
	Clause(sqlite.OnConflict(docs.ID).
		DoUpdate(sqlb.SetExpr(docs.Title, sqlite.Excluded(docs.Title))).
		Clause()).
	Build()

hits, err := sqlite.Select().Columns(sqlb.Bind(docs.ID, &id)).
	From(docs.Table).
	Where(sqlite.Match(docs.Body, userQuery)).
	Build()

MySQL: the upsert in MySQL's spelling, which fires on any unique key, and a full-text match.

import "github.com/ubgo/sqlb/mysql"

up, err := mysql.Insert(docs.Table).
	Row(sqlb.Set(docs.ID, "d1"), sqlb.Set(docs.Title, "Ada")).
	Clause(mysql.OnDuplicateKeyUpdate(sqlb.SetExpr(docs.Title, mysql.New(docs.Title)))).
	Build()

hits, err := mysql.Select().Columns(sqlb.Bind(docs.ID, &id)).
	From(docs.Table).
	Where(sqlb.Greater(mysql.Match(userQuery, mysql.NaturalLanguage, docs.Body), 0.0)).
	Build()

A Postgres extension is a sub-package built on the guarded constructors pg exports, so adding one never edits pg. Here a pgvector distance, ordered like any other expression:

import "github.com/ubgo/sqlb/pg/vector"

dist := vector.L2(docs.Embedding, vector.ParamVector(query))

near, err := pg.Select().Columns(sqlb.Bind(docs.ID, &id)).
	From(docs.Table).
	Order(sqlb.OrderBy(dist, sqlb.SortAsc)).
	Limit(10).
	Build()
Where to go next

Every snippet above is the shape a test in this repository builds and checks. The deeper versions — predicates, aggregates, windows, CTEs, subqueries, set operations, upserts, JSON, arrays, ranges, full-text search and the eight extension packages — live one page per package under docs/, with runnable snippets throughout. docs/cross-package.md takes the join above to its full size — an auth library, a plugin that borrows one of its tables, a second library that imports nothing, and the application that consolidates all three. docs/switching-databases.md shows one codebase moving between Postgres, MySQL and SQLite by changing a dialect value, and every engine difference that had to be handled to make the answers identical. docs/engines.md is the map of which construct lives where, and docs/execution.md covers transactions, the scan readers and concurrency.

FAQ

Is sqlb an ORM?

No. There is no relation graph, no code generation, no derived DDL, no lazy loading and no migrations. It builds SQL text and bound arguments, and executes them through database/sql. If you want an ORM, use one.

Does it generate code?

No. There is no build step and nothing to regenerate when a table changes. You declare columns as package-level values, and the compiler does the checking a generator would otherwise do.

Which drivers does it work with?

Any database/sql driver. The core takes a DBTX interface that both *sql.DB and *sql.Tx satisfy, so pgx through its stdlib wrapper, modernc SQLite, mattn SQLite and clickhouse-go all work. The core has no driver and no opinion about one; contrib/driver-sqlite is a convenience, not a requirement.

What happens when sqlb has no helper for the SQL I need?

You write it yourself and stay inside the type system. sqlb.Fn[R] is the typed door for any function call, and each engine package exports guarded constructors so an application can add a missing helper without waiting for a release. A missing helper is never a reason to go back to strings.

How does it stop me using a Postgres feature on SQLite?

Two ways, honestly distinguished. An engine clause is typed to its dialect and statement kind, so pg.ForUpdate() on a SQLite statement does not compile. An engine expression records its own name while rendering; under another dialect, Build returns ErrUnsupportedDialect naming the construct, for example vector.L2 under dialect "sqlite". Both failures happen in your process, before anything reaches a database.

Can I build a query once and reuse it across requests? Is a Query thread-safe?

Sequentially, yes. Concurrently, no: a Query is not safe for concurrent use, and running one from two goroutines is a data race. It holds pointers into the variables you bound, so both write to the same memory and interleave each other's rows. go test -race reports it against those variables. Build it where its destination lives, per call; building is just assembling a string. One and Each refuse an overlapping execution with ErrQueryInUse rather than returning corrupted results. The scan package's readers, which allocate per row instead of writing through your pointers, are safe to share.

Does importing sqlb pull anything into my dependency tree?

Nothing. The core imports only the Go standard library, and a gate fails the build if a third-party requirement ever appears in its go.mod. Anything that needs a dependency is a separate module under contrib/.

Is it production ready?

It is pre-GA and versioned v0.x, so the API may still break. The behaviour is heavily pinned: several hundred statements have their exact SQL checked by tests, deliberately wrong code is compiled by a gate that fails if it succeeds, and execution is proven against real databases on both SQLite and Postgres — not just rendered. Read COVERAGE.md for the measured numbers and, more usefully, for what coverage does not measure.

License

Apache License 2.0. Free for any use, including commercial, with a patent grant and the attribution the license requires.

Go SQL query builder · typed SQL for Go · generics · database/sql · zero dependencies · ORM alternative · PostgreSQL · SQLite · ClickHouse · pgvector · pg_trgm · ltree · pgcrypto · PostGIS · query builder for library authors · compile-checked joins and scan targets · open source, Apache-2.0

Documentation

Overview

Package sqlb is a typed SQL layer for Go libraries that own database tables and cannot depend on an ORM.

The problem it solves is a specific one. A library such as an auth package ships its own tables, and the application that embeds it has tables of its own. The library must let the application join across that boundary, filter on its own columns and scan into its own structs, without the library knowing any of it, and without dragging in a code generator or an ORM that the application may already have a different copy of.

One package declares tables with typed columns; another joins them, and the compiler checks every column, join condition and scan target:

var Users = sqlb.Table{Name: "app_user", Alias: "u"}
var (
	UserID    = sqlb.Col[string](Users, "id")
	UserEmail = sqlb.Col[string](Users, "email")
)

var id, email string
q, err := sqlb.NewSelect(sqlb.PostgresDialect{}).
	Columns(sqlb.Bind(UserID, &id), sqlb.Bind(UserEmail, &email)).
	From(Users).
	Where(sqlb.Eq(UserEmail, "a@example.com")).
	Build()
err = sqlb.One(ctx, db, q)

Nothing is passed to One for the destination, because the query already carries it. Bind captured the pointer, Build collected the pointers in SELECT order, and Scan writes straight through them. The column list and the scan targets come from one slice, so they cannot drift.

The condition that follows from it: a Query is not safe for concurrent use. Executing one from two goroutines is a data race, because those pointers are the caller's variables. Build the query where its destination lives, per call. One and Each refuse an overlapping execution with ErrQueryInUse rather than interleaving rows, which is detection and not prevention.

github.com/ubgo/sqlb/scan is the other strategy and does not have that hazard: it returns values rather than writing through pointers, so one Reader is safe to share across any number of concurrent executions. Neither is deprecated. Binding is shorter to write and fits most queries; scanning is what a query shared between goroutines, cached, or held in a struct field needs.

This package is a facade

Everything here is an alias or a forwarder. The implementation lives in one package per layer, and a caller who wants only part of it can import that part directly:

render   placeholders, identifier quoting, bound values; standard library only
dialect  one package per engine: dialect/postgres, dialect/sqlite, dialect/mysql, dialect/clickhouse
expr     tables, typed columns, predicates, aggregates, windows
stmt     SELECT, INSERT, UPDATE, DELETE, clause slots, Query
exec     running a statement and iterating its rows
bind     results written through caller pointers, re-exported here
scan     results returned as values, imported directly
schema   what tables exist, for generators and migrations, imported directly

Every layer compiles and is useful without anything above it and never imports upward, which is what lets a package outside this module replace any one of them. scan and schema are absent from this facade because their names would collide with ones already here, not because they are lesser.

What belongs here

The core holds only what Postgres, MySQL 8, SQLite and ClickHouse all accept with the same syntax and the same meaning. Anything one engine spells differently lives in that engine's package, github.com/ubgo/sqlb/pg, github.com/ubgo/sqlb/sqlite or github.com/ubgo/sqlb/ch, even when two engines agree.

MySQL is a reference engine only. This module has no MySQL dialect and cannot render for it, so that quarter of the rule is checked by reading MySQL's documentation rather than by building a statement. The other three are real: each has a dialect package under dialect/ and a construct package. Every engine construct records its name through Args.Unsupported, so rendering it under the wrong dialect fails at Build with ErrUnsupportedDialect rather than at the database.

What it is not

It is not an ORM. There is no relation graph, no code generation, no derived DDL and no lazy loading in this module, and there will not be.

That is a statement about this module rather than about the repository. github.com/ubgo/sqlb/schema describes what tables exist, and contrib modules act on that description, which is how DDL gets derived without any of it leaking down here. The separation is the point: a library that only needs to build a query pays for none of it.

Zero dependencies

This module imports nothing outside the standard library, and a CI gate fails the build if that changes. A driver is a dependency, so driver integrations are separate modules under contrib/, and each one proves itself by running the shared conformance suite in github.com/ubgo/sqlb/sqlbtest against a real database.

Index

Constants

View Source
const (
	FrameRange          = expr.FrameRange
	FrameRows           = expr.FrameRows
	LogicAnd            = expr.LogicAnd
	LogicNot            = expr.LogicNot
	LogicOr             = expr.LogicOr
	OpAdd               = expr.OpAdd
	OpConcat            = expr.OpConcat
	OpDiv               = expr.OpDiv
	OpEq                = expr.OpEq
	OpGreater           = expr.OpGreater
	OpGreaterEq         = expr.OpGreaterEq
	OpIn                = expr.OpIn
	OpIsDistinctFrom    = expr.OpIsDistinctFrom
	OpIsFalse           = expr.OpIsFalse
	OpIsNotDistinctFrom = expr.OpIsNotDistinctFrom
	OpIsNotNull         = expr.OpIsNotNull
	OpIsNull            = expr.OpIsNull
	OpIsTrue            = expr.OpIsTrue
	OpIsUnknown         = expr.OpIsUnknown
	OpLess              = expr.OpLess
	OpLessEq            = expr.OpLessEq
	OpLike              = expr.OpLike
	OpMod               = expr.OpMod
	OpMul               = expr.OpMul
	OpNotEq             = expr.OpNotEq
	OpNotIn             = expr.OpNotIn
	OpNotLike           = expr.OpNotLike
	OpSub               = expr.OpSub
	SortAsc             = expr.SortAsc
	SortDesc            = expr.SortDesc
	NullsFirst          = expr.NullsFirst
	NullsLast           = expr.NullsLast
)

Constants re-exported from the expr layer. Go cannot alias a constant, so each is a new constant with the same value.

View Source
const (
	DialectPostgres   = render.DialectPostgres
	DialectSQLite     = render.DialectSQLite
	DialectClickHouse = render.DialectClickHouse
	DialectMySQL      = render.DialectMySQL
	DialectUnknown    = render.DialectUnknown
)

The engine names this module ships. Not a closed set; see render.

View Source
const (
	JoinCross        = stmt.JoinCross
	JoinInner        = stmt.JoinInner
	JoinLeft         = stmt.JoinLeft
	JoinRight        = stmt.JoinRight
	SlotAfterColumns = stmt.SlotAfterColumns
	SlotAfterFrom    = stmt.SlotAfterFrom
	SlotAfterGroupBy = stmt.SlotAfterGroupBy
	SlotAfterHaving  = stmt.SlotAfterHaving
	SlotAfterInsert  = stmt.SlotAfterInsert
	SlotAfterOffset  = stmt.SlotAfterOffset
	SlotAfterOrder   = stmt.SlotAfterOrder
	SlotAfterSelect  = stmt.SlotAfterSelect
	SlotAfterSet     = stmt.SlotAfterSet
	SlotAfterTarget  = stmt.SlotAfterTarget
	SlotAfterVerb    = stmt.SlotAfterVerb
	SlotAfterWhere   = stmt.SlotAfterWhere
	SlotBeforeOrder  = stmt.SlotBeforeOrder
	SlotBeforeValues = stmt.SlotBeforeValues
	SlotBeforeWhere  = stmt.SlotBeforeWhere
	SlotInsertBody   = stmt.SlotInsertBody
	SlotTail         = stmt.SlotTail
)

Constants re-exported from the stmt layer. Go cannot alias a constant, so each is a new constant with the same value.

Variables

View Source
var (
	// ErrNoRows is returned by One when the statement matched nothing.
	ErrNoRows = exec.ErrNoRows
	// ErrMultipleRows is returned by One when a statement expected to be
	// unique matched more than one row.
	ErrMultipleRows = exec.ErrMultipleRows
)

Variables re-exported from the execution layer.

View Source
var (
	OpValues            = expr.OpValues
	SortDirectionValues = expr.SortDirectionValues
	NullsOrderValues    = expr.NullsOrderValues
	FrameModeValues     = expr.FrameModeValues
	ErrUnknownValue     = expr.ErrUnknownValue
)

Variables re-exported from the expr layer.

View Source
var (
	ErrDuplicateAlias  = stmt.ErrDuplicateAlias
	ErrJoinWithoutFrom = stmt.ErrJoinWithoutFrom
	ErrNoAssignments   = stmt.ErrNoAssignments
	ErrNoColumns       = stmt.ErrNoColumns
	ErrNoReturning     = stmt.ErrNoReturning
	ErrNoTargets       = stmt.ErrNoTargets
	ErrNoWhere         = stmt.ErrNoWhere
	ErrNotAColumn      = stmt.ErrNotAColumn
	ErrQueryInUse      = stmt.ErrQueryInUse
	ErrRowWidth        = stmt.ErrRowWidth
	ErrSetOpArity      = stmt.ErrSetOpArity
	ErrUnknownSlot     = stmt.ErrUnknownSlot
	JoinTypeValues     = stmt.JoinTypeValues
	SlotValues         = stmt.SlotValues
)

Variables re-exported from the stmt layer.

View Source
var ErrNoLikeEscape = expr.ErrNoLikeEscape

ErrNoLikeEscape is recorded when a dialect cannot express an ESCAPE clause, so a user's text cannot be escaped safely.

View Source
var ErrNoLiteralQuoting = render.ErrNoLiteralQuoting

ErrNoLiteralQuoting reports a literal requested of a dialect without the capability.

View Source
var ErrNotInlinable = render.ErrNotInlinable

ErrNotInlinable reports a value a dialect will not write as a literal.

View Source
var ErrUnsupportedDialect = render.ErrUnsupportedDialect

ErrUnsupportedDialect is returned by Build when a construct was rendered through a dialect that does not support it.

View Source
var ErrZeroExpr = expr.ErrZeroExpr

ErrZeroExpr is recorded when a zero Expr is rendered. See expr.ErrZeroExpr.

Functions

func CheckedFunctionName

func CheckedFunctionName(a *render.Args, name, what string) string

CheckedFunctionName writes a function name, or fails the render naming it. A name may be schema-qualified, which is why it is not one identifier. See expr.CheckedFunctionName.

func CheckedKeyword

func CheckedKeyword(a *render.Args, s, what string) string

CheckedKeyword writes a keyword, or fails the render naming it. It is what makes JoinType and SetOp open in their set while checked in their text. See expr.CheckedKeyword.

func CheckedOperator

func CheckedOperator(a *render.Args, op, what string) string

CheckedOperator writes an operator, or fails the render naming it.

An operator cannot be quoted, so it reaches the statement text as the author wrote it; the shape check is what keeps that from being a hole. See expr.CheckedOperator.

func DialectName

func DialectName(d Dialect) string

DialectName reports the engine name of d, or DialectUnknown.

func Each

func Each(ctx context.Context, db DBTX, q Query, fn func() error) error

Each executes q and scans every row into the bound destinations, calling fn

func EngineGuard

func EngineGuard(a *Args, engine, construct string) bool

EngineGuard reports whether a is rendering for engine, and records the

func EngineList

func EngineList(a *Args, items []SQLer) string

EngineList renders SQLers comma-separated, for a constructor assembling its

func EscapeLike

func EscapeLike(s string) string

EscapeLike escapes the characters LIKE treats as syntax, so a value can be used inside a pattern the caller builds.

func One

func One(ctx context.Context, db DBTX, q Query) error

One executes q and scans exactly one row into the destinations bound when the

func PredicateSQL

func PredicateSQL(p Predicate, a *render.Args) string

PredicateSQL renders p to a string with the statement's Args. Engine

func Quote

func Quote(s string, q byte) string

Quote wraps s in q and doubles any embedded q.

func QuoteList

func QuoteList(d render.Dialect, names []string) string

QuoteList renders identifiers comma-separated and quoted, for column lists.

func Row

func Row(items ...render.SQLer) render.SQLer

Row renders a row constructor (a, b, c), for row comparisons and keyset

func Targets

func Targets(bs []Binding) []any

Targets returns the scan destinations of a binding list, in order.

Types

type AggOption

type AggOption = expr.AggOption

AggOption modifies an aggregate call.

func Distinct

func Distinct() AggOption

Distinct adds DISTINCT inside the aggregate: count(DISTINCT x).

type AggSpec

type AggSpec = expr.AggSpec

AggSpec is what an aggregate call can carry beyond its arguments. The

type AnyClause

type AnyClause = stmt.AnyClause

AnyClause is a clause with no dialect or kind in its type: what a Join carries for a per-table hint, and what every typed clause also satisfies.

type AnySelect

type AnySelect = stmt.AnySelect

AnySelect is a Select whose engine is only known at runtime; it accepts every universal construct and no engine clause.

type Args

type Args = render.Args

Args collects bound values during one render and carries the first error.

func NewArgs

func NewArgs(d Dialect) *Args

NewArgs starts an empty argument list for d.

func NewInlineArgs

func NewInlineArgs(d Dialect) *Args

NewInlineArgs returns Args whose Next writes every value as a literal, for a statement that takes no parameters. See render.NewInlineArgs.

type Assignment

type Assignment = stmt.Assignment

Assignment is one column = value pair for INSERT and UPDATE. Built with

func AssignTarget

func AssignTarget(col string, target func(a *Args) string, value SQLer) Assignment

AssignTarget makes an assignment whose target is an expression over col,

func Set

func Set[T any](col Expr[T], v T) Assignment

Set assigns a bound value to a column of the same Go type.

func SetDefault

func SetDefault[T any](col Expr[T]) Assignment

SetDefault assigns DEFAULT.

func SetExpr

func SetExpr[T any](col Expr[T], e Expr[T]) Assignment

SetExpr assigns an expression of the column's type: SET n = n + 1.

type Binding

type Binding = stmt.Binding

Binding is a select or RETURNING item that carries a scan destination.

It is an interface defined by the statement layer, which knows only that an item renders and may carry a target. That the target is a pointer into a caller variable is decided by the bind package, which is also where the concurrency warning lives, because it is the layer that captured the memory.

func Bind

func Bind[T any](e Expr[T], dst *T) Binding

Bind pairs e with dst. The compiler rejects a dst whose type is not *T.

The pointer is captured, not the value, and it lives as long as the query does. Two consequences worth knowing before they bite: Each writes every row through the same pointer, so a callback keeping a row must copy it; and a query built this way is NOT SAFE FOR CONCURRENT USE, because two goroutines would write through these pointers at once, which is a DATA RACE. Build the query where its destination lives. See One and ErrQueryInUse, and see github.com/ubgo/sqlb/scan for the strategy that returns values instead.

func Bindings

func Bindings(groups ...[]Binding) []Binding

Bindings concatenates binding groups, which is how one query scans into structs owned by several packages that do not import each other.

type CTE

type CTE = stmt.CTE

CTE is one common table expression. Columns renders the optional column

type CaseBuilder

type CaseBuilder[T any] = expr.CaseBuilder[T]

CaseBuilder accumulates a searched CASE. Else yields Expr[T]; End yields

func Case

func Case[T any]() CaseBuilder[T]

Case starts a searched CASE expression.

type CaseInsensitiveLike

type CaseInsensitiveLike = render.CaseInsensitiveLike

CaseInsensitiveLike is satisfied by a dialect with ILIKE. See render.CaseInsensitiveLike for why the fold predicates ask for it.

type Clause

type Clause[D render.Dialect, K any] = stmt.Clause[D, K]

Clause is a statement part that is neither an expression nor a predicate.

func EngineClause

func EngineClause[D render.Dialect, K any](construct string, slot Slot, render func(a *Args) string) Clause[D, K]

EngineClause makes a guarded clause for a statement slot.

type ClickHouseDialect

type ClickHouseDialect = clickhouse.Dialect

ClickHouseDialect renders ? placeholders and backquoted identifiers. It deliberately does not satisfy Returning; ClickHouse has no RETURNING.

type CombinedAlter

type CombinedAlter = render.CombinedAlter

CombinedAlter is satisfied by dialects that accept several actions in one ALTER TABLE. See render.CombinedAlter.

type DBTX

type DBTX = exec.DBTX

DBTX is the subset of database/sql that both *sql.DB and *sql.Tx satisfy.

type Delete

type Delete[D render.Dialect] = stmt.Delete[D]

Delete accumulates a DELETE statement with the same WHERE-or-All rule as Update.

func NewDelete

func NewDelete[D render.Dialect](d D) *Delete[D]

NewDelete starts a DELETE for d.

type DeleteKind

type DeleteKind = stmt.DeleteKind

DeleteKind marks a clause for DELETE.

type Dialect

type Dialect = render.Dialect

Dialect is the two things SQL engines disagree on: how a bound parameter is written and how an identifier is quoted. See render.Dialect.

type Expr

type Expr[T any] = expr.Expr[T]

Expr is a SQL expression that evaluates to the Go type T.

func Add

func Add[T Number](l, r Expr[T]) Expr[T]

Add renders l + r.

func Aggregate

func Aggregate[R any](name string, args []render.SQLer, opts ...AggOption) Expr[R]

Aggregate renders name(args...) with the options applied, typed R. It is

func As

func As[T any](e Expr[T], name string) Expr[T]

As aliases a select item: expr AS name. Needed for a CTE or derived table

func AssertNotNull

func AssertNotNull[T any](e Expr[*T]) Expr[T]

AssertNotNull reinterprets a nullable expression as T without changing the

func Avg

func Avg[T Number](e Expr[T], opts ...AggOption) Expr[float64]

Avg is float64. SQL returns numeric for integer input; scanning numeric

func Binary

func Binary[R any](left render.SQLer, op string, right render.SQLer) Expr[R]

Binary renders "(left op right)" typed R. Engine packages use it for their

func BoolExpr

func BoolExpr(p Predicate) Expr[bool]

BoolExpr turns a predicate into a boolean expression, so it can be

func Cast

func Cast[R, T any](e Expr[T], sqlType string) Expr[R]

Cast renders CAST(e AS sqlType) and re-types the expression as R. The SQL

func Coalesce

func Coalesce[T any](first Expr[T], rest ...Expr[T]) Expr[T]

Coalesce renders COALESCE(first, rest...). The result type is T; the

func Col

func Col[T any](t Table, name string) Expr[T]

Col declares a column of table t with Go type T. It renders "alias.name".

func Concat

func Concat(parts ...Expr[string]) Expr[string]

Concat renders concat(a, b, ...), the function form, because the ||

func Count

func Count(opts ...AggOption) Expr[int64]

Count renders count(*), int64 and never null.

func CountOf

func CountOf[T any](e Expr[T], opts ...AggOption) Expr[int64]

CountOf renders count(e).

func Div

func Div[T Number](l, r Expr[T]) Expr[T]

Div renders l / r, which truncates on integer operands. Cast one side for

func EngineBinOp

func EngineBinOp[R any](engine, construct, op string, l, r SQLer) Expr[R]

EngineBinOp renders "left op right" as a guarded expression typed R.

func EngineExpr

func EngineExpr[T any](engine, construct string, render func(a *Args) string) Expr[T]

EngineExpr makes a guarded expression typed T from a render function. It is

func EngineFn

func EngineFn[R any](engine, construct, name string, args ...SQLer) Expr[R]

EngineFn renders a guarded function call typed R.

func EngineLit

func EngineLit[T any](engine, construct, sql string) Expr[T]

EngineLit makes a guarded expression from a constant fragment.

func Fn

func Fn[R any](name string, args ...render.SQLer) Expr[R]

Fn renders a function call name(args...) typed R. It is the typed door for

func Literal

func Literal[T any](sql string) Expr[T]

Literal is a constant SQL fragment typed as T.

func Lower

func Lower(e Expr[string]) Expr[string]

Lower wraps a string expression in lower(). Used on both sides of an

func Max

func Max[T any](e Expr[T], opts ...AggOption) Expr[T]

Max renders max(e).

func Min

func Min[T any](e Expr[T], opts ...AggOption) Expr[T]

Min renders min(e). Typed as T rather than *T: a group that exists has at

func Mod

func Mod[T Number](l, r Expr[T]) Expr[T]

Mod renders l % r.

func Mul

func Mul[T Number](l, r Expr[T]) Expr[T]

Mul renders l * r.

func Neg

func Neg[T Number](e Expr[T]) Expr[T]

Neg renders (-e), parenthesised so it composes without a precedence

func New

func New[T any](render func(a *render.Args) string) Expr[T]

New makes an expression from a render function. It is the single typed

func NullIf

func NullIf[T any](l, r Expr[T]) Expr[*T]

NullIf renders NULLIF(a, b). It is always nullable, so the result is *T.

func Nullable

func Nullable[T any](e Expr[T]) Expr[*T]

Nullable reinterprets an expression of T as *T without changing the SQL.

func OutputCol

func OutputCol[T any](name string) Expr[T]

OutputCol references a select-list alias by name, typed T. It is how an

func Over

func Over[T any](e Expr[T], w Window) Expr[T]

Over turns any expression, in practice an aggregate, into a window

func OverNamed

func OverNamed[T any](e Expr[T], name string) Expr[T]

OverNamed is Over with a named window.

func Param

func Param[T any](v T) Expr[T]

Param binds a Go value as an expression of its own type.

func Sub

func Sub[T Number](l, r Expr[T]) Expr[T]

Sub renders l - r.

func Subquery

func Subquery[T any, D render.Dialect](s *Select[D]) Expr[*T]

Subquery uses a Select as a scalar expression: (SELECT ...). The author

func Sum

func Sum[T Number](e Expr[T], opts ...AggOption) Expr[T]

Sum keeps the operand type. SQL widens sum(int4) to int8; database/sql

func Upper

func Upper(e Expr[string]) Expr[string]

Upper wraps a string expression in upper(), universal as lower() is.

type FilteredDelete

type FilteredDelete[D render.Dialect] = stmt.FilteredDelete[D]

FilteredDelete is a DELETE whose rows are stated, the only form that has Build; Where and All return it.

func DeleteReturning

func DeleteReturning[D ReturningDialect](s *FilteredDelete[D], bs ...Binding) *FilteredDelete[D]

DeleteReturning adds RETURNING to a DELETE on a dialect that has it.

type FilteredUpdate

type FilteredUpdate[D render.Dialect] = stmt.FilteredUpdate[D]

FilteredUpdate is an UPDATE whose rows are stated, the only form that has Build; Where and All return it.

func UpdateReturning

func UpdateReturning[D ReturningDialect](s *FilteredUpdate[D], bs ...Binding) *FilteredUpdate[D]

UpdateReturning adds RETURNING to an UPDATE on a dialect that has it.

type FrameBound

type FrameBound = expr.FrameBound

FrameBound is one end of a frame. Use the constructors below.

func CurrentRow

func CurrentRow() FrameBound

CurrentRow bounds a frame at the current row. Under RANGE that means the

func Following

func Following(n int) FrameBound

Following bounds a frame n rows forward.

func FollowingExpr

func FollowingExpr(sql string) FrameBound

FollowingExpr bounds a frame with an expression rather than a count, for a

func Preceding

func Preceding(n int) FrameBound

Preceding bounds a frame n rows back under ROWS, or n units back under

func PrecedingExpr

func PrecedingExpr(sql string) FrameBound

PrecedingExpr and FollowingExpr take a constant SQL expression as the

func UnboundedFollowing

func UnboundedFollowing() FrameBound

UnboundedFollowing ends a frame at the last row of the partition.

func UnboundedPreceding

func UnboundedPreceding() FrameBound

UnboundedPreceding starts a frame at the first row of the partition.

type FrameExclude

type FrameExclude = expr.FrameExclude

FrameExclude is the EXCLUDE part of a frame. The constants are in pg,

type FrameMode

type FrameMode = expr.FrameMode

FrameMode selects ROWS or RANGE for a window frame. GROUPS is Postgres

type Guard

type Guard = stmt.Guard

Guard is the execution guard an engine package's statement builder owns, so every Query it and its derived statements build shares one.

func NewGuard

func NewGuard() Guard

NewGuard creates a guard for one builder and everything derived from it.

type Insert

type Insert[D render.Dialect] = stmt.Insert[D]

Insert accumulates an INSERT statement.

func NewInsert

func NewInsert[D render.Dialect](d D) *Insert[D]

NewInsert starts an INSERT for d.

type InsertAliasing

type InsertAliasing = render.InsertAliasing

InsertAliasing is satisfied by dialects that accept "INSERT INTO t AS alias".

type InsertKind

type InsertKind = stmt.InsertKind

InsertKind marks a clause for INSERT.

type InsertReady

type InsertReady[D render.Dialect] = stmt.InsertReady[D]

InsertReady is an INSERT with a body, the only form that has Build; Row, Select and Body return it.

func InsertReturning

func InsertReturning[D ReturningDialect](s *InsertReady[D], bs ...Binding) *InsertReady[D]

InsertReturning adds RETURNING to an INSERT on a dialect that has it.

type Join

type Join = stmt.Join

Join brings a source into a statement. Table is the common case; Source

type JoinType

type JoinType = stmt.JoinType

JoinType is the SQL join keyword. It is a distinct string type rather than a

type LikeEscaping

type LikeEscaping = render.LikeEscaping

LikeEscaping is satisfied by dialects that accept an ESCAPE clause on LIKE. See render.LikeEscaping for why escaping a pattern is not portable.

type LiteralQuoting

type LiteralQuoting = render.LiteralQuoting

LiteralQuoting is satisfied by a dialect that can write a value as a literal the engine reads back exactly. See render.LiteralQuoting.

type MySQLDialect

type MySQLDialect = mysql.Dialect

MySQLDialect renders ? placeholders and backquoted identifiers. It does not satisfy Returning; MySQL has no RETURNING.

type Named

type Named = render.Named

Named is satisfied by dialects that can name their engine. See render.Named.

type NullsOrder

type NullsOrder = expr.NullsOrder

NullsOrder is where NULLs sort relative to values; see expr.NullsOrder.

type Number

type Number = expr.Number

Number is the set of Go types that arithmetic operators accept.

type NumericParameters

type NumericParameters = render.NumericParameters

NumericParameters is satisfied by a dialect that widens an untyped integer parameter beside a DECIMAL. See render.NumericParameters for the MySQL measurement behind it.

type Option

type Option = stmt.Option

Option extends a statement that a library built, from code that the library

func WithColumns

func WithColumns(bs ...Binding) Option

WithColumns adds bindings after the library's own, so the library's scan

func WithJoin

func WithJoin(js ...Join) Option

WithJoin adds joins to the statement.

func WithOrder

func WithOrder(ts ...OrderTerm) Option

WithOrder adds order terms after the library's own.

func WithWhere

func WithWhere(ps ...Predicate) Option

WithWhere adds predicates. They are ANDed with the library's own.

type OrderTerm

type OrderTerm = expr.OrderTerm

OrderTerm is one ORDER BY item. Nulls and Using are hooks the core

func OrderBy

func OrderBy[T any](e Expr[T], dir SortDirection) OrderTerm

OrderBy makes an order term from any typed expression.

type PostgresDialect

type PostgresDialect = postgres.Dialect

PostgresDialect renders $1, $2 placeholders and double-quoted identifiers.

The three dialects below are aliases because each one now lives in its own package under dialect/, built the way a dialect from another repository is built: importing the render layer and nothing else. Keeping the old names here means the split cost no caller an import, and it keeps a one-import entry point for code that just needs to say which engine it targets.

type Predicate

type Predicate = expr.Predicate

Predicate is a boolean condition for WHERE or ON.

func And

func And(preds ...Predicate) Predicate

And groups predicates with AND. Empty renders TRUE.

func Between

func Between[T any](e Expr[T], lo, hi T) Predicate

Between renders e BETWEEN lo AND hi with bound ends.

func Compare

func Compare(left render.SQLer, op string, right render.SQLer) Predicate

Compare renders "left op right" as a predicate. Engine packages use it for

func Contains

func Contains(e Expr[string], value string) Predicate

Contains renders e LIKE '%value%' with value escaped, the predicate behind a search box.

func ContainsFold

func ContainsFold(e Expr[string], value string) Predicate

ContainsFold is Contains regardless of case: ILIKE on a dialect that has it, lower() LIKE lower() elsewhere.

func EndsWith

func EndsWith(e Expr[string], value string) Predicate

EndsWith renders e LIKE '%value' with value escaped.

func EndsWithFold

func EndsWithFold(e Expr[string], value string) Predicate

EndsWithFold is EndsWith regardless of case.

func EngineBinPred

func EngineBinPred(engine, construct, op string, l, r SQLer) Predicate

EngineBinPred renders "left op right" as a guarded predicate.

func EnginePred

func EnginePred(engine, construct string, render func(a *Args) string) Predicate

EnginePred makes a guarded predicate from a render function.

func Eq

func Eq[T any](e Expr[T], v T) Predicate

Eq and its family compare an expression with a bound value of the same Go

func EqExpr

func EqExpr[T any](left, right Expr[T]) Predicate

EqExpr compares two expressions of the same Go type. It is the join

func EqFold

func EqFold(e Expr[string], value string) Predicate

EqFold matches e against value regardless of case, lower(e) = lower(value) on every engine. See expr.EqFold for the index and collation notes.

func Exists

func Exists[D render.Dialect](s *Select[D]) Predicate

Exists and NotExists. The subquery may be correlated: it references the

func False

func False() Predicate

False renders FALSE, which fails a query closed. It is what an empty IN

func Greater

func Greater[T any](e Expr[T], v T) Predicate

Greater renders e > $n.

func GreaterEq

func GreaterEq[T any](e Expr[T], v T) Predicate

GreaterEq renders e >= $n.

func GreaterEqExpr

func GreaterEqExpr[T any](left, right Expr[T]) Predicate

GreaterEqExpr renders left >= right.

func GreaterExpr

func GreaterExpr[T any](left, right Expr[T]) Predicate

GreaterExpr renders left > right.

func In

func In[T any](e Expr[T], values ...T) Predicate

In binds every value. An empty list renders FALSE.

func InExprs

func InExprs[T any](e Expr[T], items ...Expr[T]) Predicate

InExprs renders e IN (x, y, ...) over expressions, for a membership test

func InFold

func InFold(e Expr[string], values ...string) Predicate

InFold matches e against any of values regardless of case; an empty list is FALSE.

func InSubquery

func InSubquery[T any, D render.Dialect](e Expr[T], s *Select[D]) Predicate

InSubquery renders e IN (SELECT ...). The subquery must select one item

func IsNotNull

func IsNotNull[T any](e Expr[T]) Predicate

IsNotNull renders e IS NOT NULL.

func IsNull

func IsNull[T any](e Expr[T]) Predicate

IsNull and IsNotNull test for NULL. Typed on T only so they accept any Expr.

func Less

func Less[T any](e Expr[T], v T) Predicate

Less renders e < $n.

func LessEq

func LessEq[T any](e Expr[T], v T) Predicate

LessEq renders e <= $n.

func LessEqExpr

func LessEqExpr[T any](left, right Expr[T]) Predicate

LessEqExpr renders left <= right.

func LessExpr

func LessExpr[T any](left, right Expr[T]) Predicate

LessExpr renders left < right.

func Like

func Like(e Expr[string], pattern string) Predicate

Like and NotLike. The pattern is bound, never in the SQL text.

func Not

func Not(p Predicate) Predicate

Not negates a predicate, always parenthesised.

func NotBetween

func NotBetween[T any](e Expr[T], lo, hi T) Predicate

NotBetween is the negated form.

func NotContains

func NotContains(e Expr[string], value string) Predicate

NotContains renders e NOT LIKE '%value%' with value escaped.

func NotEq

func NotEq[T any](e Expr[T], v T) Predicate

NotEq renders e <> $n. NULL <> v is NULL, not true, so this does not match

func NotEqExpr

func NotEqExpr[T any](left, right Expr[T]) Predicate

NotEqExpr renders left <> right.

func NotExists

func NotExists[D render.Dialect](s *Select[D]) Predicate

NotExists renders NOT EXISTS (SELECT ...), the anti-join. It is usually

func NotIn

func NotIn[T any](e Expr[T], values ...T) Predicate

NotIn binds every value. An empty list renders TRUE.

func NotInFold

func NotInFold(e Expr[string], values ...string) Predicate

NotInFold matches e against none of values regardless of case; an empty list is TRUE.

func NotInSubquery

func NotInSubquery[T any, D render.Dialect](e Expr[T], s *Select[D]) Predicate

NotInSubquery renders e NOT IN (SELECT ...). Beware the NULL rule: if the

func NotLike

func NotLike(e Expr[string], pattern string) Predicate

NotLike renders e NOT LIKE $n, the negation of Like.

func Or

func Or(preds ...Predicate) Predicate

Or groups predicates with OR. Empty renders TRUE, which is the safe value

func PredicateOf

func PredicateOf(render func(a *render.Args) string) Predicate

PredicateOf makes a Predicate from a render function. Engine packages use

func RowEq

func RowEq(l, r render.SQLer) Predicate

RowEq renders l = r for two row constructors.

func RowGreater

func RowGreater(l, r render.SQLer) Predicate

RowGreater renders l > r for two row constructors.

func RowIn

func RowIn(row render.SQLer, rows ...render.SQLer) Predicate

RowIn renders (a, b) IN ((?, ?), ...), the composite-key membership test. An empty list renders FALSE.

func RowLess

func RowLess(l, r render.SQLer) Predicate

RowLess renders l < r for two row constructors, the shape a keyset page

func RowNotIn

func RowNotIn(row render.SQLer, rows ...render.SQLer) Predicate

RowNotIn renders (a, b) NOT IN ((?, ?), ...). An empty list renders TRUE, and a NULL anywhere in the list makes the whole predicate NULL.

func StartsWith

func StartsWith(e Expr[string], value string) Predicate

StartsWith renders e LIKE 'value%' with value escaped. Prefer it to Contains where it fits: a prefix match can use an index and a leading wildcard cannot.

func StartsWithFold

func StartsWithFold(e Expr[string], value string) Predicate

StartsWithFold is StartsWith regardless of case.

func SubqueryCompare

func SubqueryCompare[D render.Dialect](e SQLer, op, quantifier string, s *Select[D]) Predicate

SubqueryCompare renders "e op [quantifier] (SELECT ...)". It is the

func True

func True() Predicate

True renders TRUE. It is the ON clause of a LATERAL join, which has no

type Query

type Query = stmt.Query

Query is a rendered statement: SQL text, the bound values in placeholder

func NewGuardedQuery

func NewGuardedQuery(g Guard, sql string, args, targets []any) Query

NewGuardedQuery is NewQuery for a builder that owns a Guard.

func NewQuery

func NewQuery(sql string, args, targets []any) Query

NewQuery assembles a rendered statement with its bound values and scan

type Returning

type Returning = render.Returning

Returning is satisfied by dialects that support INSERT ... RETURNING.

type ReturningDialect

type ReturningDialect = stmt.ReturningDialect

ReturningDialect is a dialect that has RETURNING; see stmt.ReturningDialect.

type SQLer

type SQLer = render.SQLer

SQLer is anything that can render itself into SQL text using the statement's Args.

func EngineQuoteLit

func EngineQuoteLit(s string) SQLer

EngineQuoteLit renders s as a single-quoted SQL literal with embedded

func EngineRaw

func EngineRaw(engine, construct string, render func(a *Args) string) SQLer

EngineRaw makes a guarded fragment that is neither an expression nor a

func Raw

func Raw(f func(a *Args) string) SQLer

Raw makes a SQLer from a render function, for a fragment in a position typed SQLer, such as a GROUP BY item or a raw WHERE condition.

type SQLiteDialect

type SQLiteDialect = sqlite.Dialect

SQLiteDialect renders ? placeholders and double-quoted identifiers.

type Select

type Select[D render.Dialect] = stmt.Select[D]

Select accumulates a statement. Every method appends; nothing removes. That

func NewSelect

func NewSelect[D render.Dialect](d D) *Select[D]

NewSelect starts a statement for d.

type SelectKind

type SelectKind = stmt.SelectKind

SelectKind marks a clause for SELECT.

type Slot

type Slot = stmt.Slot

Slot is a fixed position in a statement where an engine-specific clause may

type SortDirection

type SortDirection = expr.SortDirection

SortDirection is the ORDER BY direction.

type Source

type Source = expr.Source

Source is anything that can stand in FROM or be joined: a table, a derived

func Derived

func Derived(body Statement, alias string) Source

Derived makes a subquery a FROM source: "(SELECT ...) AS alias".

func EngineSource

func EngineSource(engine, construct, alias string, render func(a *Args) string) Source

EngineSource makes a guarded FROM source, for a set-returning function or

func SourceOf

func SourceOf(alias string, render func(a *render.Args) string) Source

SourceOf makes a Source from a render function and an alias. Engine

type Statement

type Statement = expr.Statement

Statement is any statement that renders into a parent's Args: a Select used

type Table

type Table = expr.Table

Table is a physical table with a fixed alias.

func Ref

func Ref(src Source) Table

Ref returns a Table value that references a source by its alias only, for

type UnrestrictedAddColumn

type UnrestrictedAddColumn = render.UnrestrictedAddColumn

UnrestrictedAddColumn is satisfied by dialects whose ADD COLUMN takes any column. See render.UnrestrictedAddColumn.

type UntypedParameters

type UntypedParameters = render.UntypedParameters

UntypedParameters is satisfied by a dialect that must be told a bound parameter's type in a function taking any. See render.UntypedParameters.

type Update

type Update[D render.Dialect] = stmt.Update[D]

Update accumulates an UPDATE statement. A WHERE or an explicit All() is

func NewUpdate

func NewUpdate[D render.Dialect](d D) *Update[D]

NewUpdate starts an UPDATE for d.

type UpdateKind

type UpdateKind = stmt.UpdateKind

UpdateKind marks a clause for UPDATE.

type WinFn

type WinFn[T any] = expr.WinFn[T]

WinFn is a window function or an aggregate that has not yet been given its

func DenseRank

func DenseRank() WinFn[int64]

DenseRank renders dense_rank(), which does not leave gaps.

func FirstValue

func FirstValue[T any](e Expr[T]) WinFn[*T]

FirstValue renders first_value(e).

func LastValue

func LastValue[T any](e Expr[T]) WinFn[*T]

LastValue renders last_value(e). With the default frame it returns the

func NewWinFn

func NewWinFn[T any](render func(a *render.Args) string) WinFn[T]

NewWinFn makes a window function from a render function. Engine packages

func NthValue

func NthValue[T any](e Expr[T], n int) WinFn[*T]

NthValue renders nth_value(e, n), 1-based within the frame.

func Rank

func Rank() WinFn[int64]

Rank renders rank(), which leaves gaps after a tie.

func RowNumber

func RowNumber() WinFn[int64]

RowNumber renders row_number(), a distinct number per row.

type Window

type Window = expr.Window

Window is a window specification: PARTITION BY, ORDER BY and a frame. It

func NewWindow

func NewWindow() Window

NewWindow starts an empty window specification.

Directories

Path Synopsis
Package bind is the result strategy that writes rows through pointers the caller supplied.
Package bind is the result strategy that writes rows through pointers the caller supplied.
Package ch holds the ClickHouse dialect and what only ClickHouse has: PREWHERE, FINAL, SAMPLE, ARRAY JOIN, LIMIT BY, SETTINGS, the argMax and uniq aggregates, the -If combinator and the toStartOf* time functions.
Package ch holds the ClickHouse dialect and what only ClickHouse has: PREWHERE, FINAL, SAMPLE, ARRAY JOIN, LIMIT BY, SETTINGS, the argMax and uniq aggregates, the -If combinator and the toStartOf* time functions.
contrib
debug module
driver-mysql module
driver-pgx module
driver-sqlite module
migrate module
structbind module
vet module
Package ddl builds the statements that create, change and remove what a library's tables are: CREATE TABLE, ALTER TABLE, DROP TABLE and the rest.
Package ddl builds the statements that create, change and remove what a library's tables are: CREATE TABLE, ALTER TABLE, DROP TABLE and the rest.
dialect
clickhouse
Package clickhouse is the ClickHouse dialect: positional placeholders and backtick-quoted identifiers.
Package clickhouse is the ClickHouse dialect: positional placeholders and backtick-quoted identifiers.
mysql
Package mysql is the MySQL dialect: how MySQL 8 writes a bound parameter and quotes an identifier, and which optional capabilities it has.
Package mysql is the MySQL dialect: how MySQL 8 writes a bound parameter and quotes an identifier, and which optional capabilities it has.
postgres
Package postgres is the Postgres dialect: how Postgres writes a bound parameter and quotes an identifier, and which optional capabilities it has.
Package postgres is the Postgres dialect: how Postgres writes a bound parameter and quotes an identifier, and which optional capabilities it has.
sqlite
Package sqlite is the SQLite dialect: positional placeholders and double-quoted identifiers.
Package sqlite is the SQLite dialect: positional placeholders and double-quoted identifiers.
examples
cookbook/app
Package app is the application side of the cross-package story: the code that owns no library, imports three of them, and consolidates what they each own into one statement.
Package app is the application side of the cross-package story: the code that owns no library, imports three of them, and consolidates what they each own into one statement.
cookbook/authlite
Package authlite is a deliberately small stand-in for a ubgo library that owns tables, for the cookbook.
Package authlite is a deliberately small stand-in for a ubgo library that owns tables, for the cookbook.
cookbook/phone
Package phone is the plugin shape: it borrows a core table under its own alias and contributes a join plus bindings to a composite user.
Package phone is the plugin shape: it borrows a core table under its own alias and contributes a join plus bindings to a composite user.
cookbook/workspace
Package workspace is the second-library shape: it owns tables that refer to a user by id, and it does not import the auth library.
Package workspace is the second-library shape: it owns tables that refer to a user by id, and it does not import the auth library.
pgsurface/advancedsql
Package advancedsql translates an external catalogue of advanced PostgreSQL queries into sqlb and checks the rendered SQL.
Package advancedsql translates an external catalogue of advanced PostgreSQL queries into sqlb and checks the rendered SQL.
pgsurface/periodcompare
Package periodcompare builds a period-comparison report over several fact tables in Postgres: sales for the current, previous and third-year periods, current stock, stock at a past snapshot date, open purchase orders and advertising spend, all keyed by one chosen dimension.
Package periodcompare builds a period-comparison report over several fact tables in Postgres: sales for the current, previous and third-year periods, current stock, stock at a past snapshot date, open purchase orders and advertising spend, all keyed by one chosen dimension.
pgsurface/snippets
Package snippets translates a catalogue of advanced PostgreSQL snippets into sqlb, section by section and in the catalogue's own order and numbering.
Package snippets translates a catalogue of advanced PostgreSQL snippets into sqlb, section by section and in the catalogue's own order and numbering.
Package exec runs a built statement against a database and iterates its rows.
Package exec runs a built statement against a database and iterates its rows.
Package expr is the typed half of the builder: a table, a column with a Go type, a bound parameter, a predicate, an aggregate, a window, and the operators that join them.
Package expr is the typed half of the builder: a table, a column with a Go type, a bound parameter, a predicate, an aggregate, a window, and the operators that join them.
Package mysql holds what only MySQL has, and is where a MySQL statement starts.
Package mysql holds what only MySQL has, and is where a MySQL statement starts.
pg
Package pg holds what only Postgres has.
Package pg holds what only Postgres has.
crypto
Package crypto is the pgcrypto extension: hashing, password hashing and symmetric encryption on the server.
Package crypto is the pgcrypto extension: hashing, password hashing and symmetric encryption on the server.
earth
Package earth is the earthdistance extension (with cube): great-circle distance and bounding boxes on the sphere.
Package earth is the earthdistance extension (with cube): great-circle distance and bounding boxes on the sphere.
fuzzy
Package fuzzy is the fuzzystrmatch extension: phonetic and edit-distance matching.
Package fuzzy is the fuzzystrmatch extension: phonetic and edit-distance matching.
ltree
Package ltree is the ltree extension: a materialized-path label tree with its own operators.
Package ltree is the ltree extension: a materialized-path label tree with its own operators.
postgis
Package postgis is the template for adding a Postgres extension without touching pg.
Package postgis is the template for adding a Postgres extension without touching pg.
trgm
Package trgm is the pg_trgm extension: trigram similarity operators and functions.
Package trgm is the pg_trgm extension: trigram similarity operators and functions.
unaccent
Package unaccent is the unaccent extension: strip diacritics before comparing or indexing text.
Package unaccent is the unaccent extension: strip diacritics before comparing or indexing text.
vector
Package vector is the pgvector extension: the four vector types, every distance operator and function, the utility functions, aggregates, arithmetic, casts, and the query-time settings.
Package vector is the pgvector extension: the four vector types, every distance operator and function, the utility functions, aggregates, arithmetic, casts, and the query-time settings.
Package render is the bottom of the builder: how a value becomes a bound parameter, how an identifier is escaped, and how the first error of a render is carried out.
Package render is the bottom of the builder: how a value becomes a bound parameter, how an identifier is escaped, and how the first error of a render is carried out.
Package scan is a result strategy that returns values instead of writing through pointers.
Package scan is a result strategy that returns values instead of writing through pointers.
Package schema describes what tables exist, so that two packages which do not know about each other can still be migrated together.
Package schema describes what tables exist, so that two packages which do not know about each other can still be migrated together.
Package sqlbtest is the conformance suite every sqlb driver integration runs.
Package sqlbtest is the conformance suite every sqlb driver integration runs.
Package sqlite holds what only SQLite has.
Package sqlite holds what only SQLite has.
Package stmt assembles SQL statements: SELECT, INSERT, UPDATE and DELETE, the clause positions they can carry, and the Query a build produces.
Package stmt assembles SQL statements: SELECT, INSERT, UPDATE and DELETE, the clause positions they can carry, and the Query a build produces.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL