bqb

package module
v0.5.0 Latest Latest
Warning

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

Go to latest
Published: Mar 18, 2026 License: MIT Imports: 9 Imported by: 0

README

Basic Query Builder

Go Reference

Hard fork of bqb with a lot of breaking changes.

Why

  1. Simple, lightweight, and fast
  2. Supports any and all syntax by the nature of how it works
  3. Doesn't require learning special syntax or operators

Basic

q := bqb.New("SELECT * FROM places WHERE id = ?", 1234)
sql, params, err := q.ToSQL()

Produces:

SELECT * FROM places WHERE id = ?
PARAMS: [1234]
Escaping ?

Use the double question mark ?? value to escape the ? in Postgres queries. For example:

q := bqb.New("SELECT * FROM places WHERE json_obj_column ?? 'key'")
sql, params, err := q.ToPgSQL()

This query uses the ? operator for jsonb types in Postgres to test an object for the presence of a key. It should not be interpreted as an escaped value by bqb.

SELECT * FROM places WHERE json_obj_column ? 'key'
PARAMS: []

Dialects

bqb supports following dialects to build queries with:

  • SQL — uses '?' placeholders, expands arrays (default)
  • SQLite — uses '?NNN' placeholders, expands arrays
  • PostgreSQL — uses '$NNN' placeholders, doesn't expand arrays

It is possible to define and use custom dialect:

var Custom = &Dialect{
	Placeholder: func(w *bytes.Buffer, i int) {
		w.WriteByte('@')
		w.WriteString(strconv.Itoa(i))
	},
	ExpandArrays: true,
}
q := bqb.New("SELECT * FROM pets WHERE name = ?", "garfield")
sel, params, err := q.Build(Custom)

Produces:

SELECT * FROM pets WHERE name = @1
PARAMS: ["garfield"]

Postgres - ToPgSQL()

The ToPgSQL method wraps Build(bqb.PostgreSQL):

q := bqb.New("DELETE FROM users").
	Space("WHERE id = ? OR name = ANY(?)", 7, []string{"delete", "remove"}).
	Space("LIMIT ?", 5)
sql, params, err := q.ToPgSQL()

Produces:

DELETE FROM users WHERE id = $1 OR name = ANY($2) LIMIT $3
PARAMS: [7, ["delete", "remove"], 5]

SQLite - ToSQLite()

The ToSQLite method wraps Build(bqb.SQLite)

q := bqb.New("INSERT INTO animals (id, name) VALUES").
	Space("(?, ?)", 1, "dolphin").
	Comma("(?, ?)", 2, "duck").
	Comma("(?, ?)", 3, "elephant")
sql, params, err := q.ToSQLite()

Produces:

INSERT INTO animals (id, name) VALUES (?1, ?2), (?3, ?4), (?5, ?6)
PARAMS: [1, "dolphin", 2, "duck", 3, "elephant"]

Types

driver.Valuer

The driver.Valuer interface is supported for types that are able to convert themselves to a sql driver value.

Embedded

It is possible to directly replace ? with a string by converting it to Embedded type:

q := bqb.New("SELECT * FROM ?", bqb.Embedded("table"))

Produces:

SELECT * FROM table
Embedder

bqb provides an Embedder interface for directly replacing ? with a string returned by the RawValue method on the Embedder implementation.

This can be useful for changing sort direction or embedding table and column names.

Since this is a raw value, special attention should be paid to ensure user-input is checked and sanitized.

Query IN

Array and slice arguments are automatically expanded for Dialects with ExpandArrays: true, except for byte arrays and byte slices.

q := bqb.New("SELECT * FROM animals WHERE name IN (?)", []string{"dolphin", "duck", "elephant"})
sql, params, _ := q.ToSQL()

Produces:

SELECT * FROM animals WHERE name IN (?, ?, ?)
PARAMS: ["dolphin", "duck", "elephant"]

You can force bqb to expand/not expand array and slice arguments regardless of the ExpandArrays settings by using Unfolded/Folded types.

JSON Arguments

There are two helper structs, JSONMap and JSONList to make JSON conversion a little simpler.

sql, err := bqb.New("INSERT INTO my_table (json_map, json_list) VALUES (?, ?)",
	bqb.JSONMap{"a": 1, "b": []string{"a", "b", "c"}},
	bqb.JSONList{"string", 1, true, nil},
).ToSQL()

Produces:

INSERT INTO my_table (json_map, json_list) VALUES (?, ?)
PARAMS: [`{"a":1,"b":["a","b","c"]}`, `["string",1,true,null]`]

Query Building

Since queries are built in an additive way by reference rather than value, it's easy to mutate a query without having to reassign the result.

Basic Example
sel := bqb.New("SELECT")
// later
sel.Space("id")
// even later
sel.Comma("age").Comma("email")

Produces:

SELECT id, age, email
Advanced Example

The Q() function returns an empty Query, that resolves to an empty string if nothing have been added via methods on the query builder. For example q := Q() will resolve to an empty string unless something have been added by one of the methods, but q.Comma("name") would make q.ToSQL() resolve to name.

The Optional(string) functions acts the same way as the Q() function, except that it prepends a prefix and a space to the text if something have been added by one of the methods. For example q := Optional("SELECT") will resolve to an empty string if nothing have been added to the query, but q.Space("* FROM my_table") would make q.ToSQL() resolve to SELECT * FROM my_table.

targets := bqb.Q()
if getName {
	targets.Comma("name")
}
if getId {
	targets.Comma("id")
}
if !getName && !getId {
	targets.Comma("*")
}

where := bqb.Optional("WHERE")
if filterAdult {
	adultCond := bqb.New("name = ?", "adult")
	if ageCheck {
		adultCond.And("age > ?", 20)
	}
	where.And("(?)", adultCond)
}
if filterChild {
	where.Or("(name = ? AND age < ?)", "youth", 21)
}

q := bqb.New("SELECT ? FROM my_table ?", targets, where).Space("LIMIT ?", 10)

Assuming all values are true, the query would look like:

SELECT name, id FROM my_table WHERE (name = 'adult' AND age > 20) OR (name = 'youth' AND age < 21) LIMIT 10

If getName and getId are false, the query would be:

SELECT * FROM my_table WHERE (name = 'adult' AND age > 20) OR (name = 'youth' AND age < 21) LIMIT 10

If filterAdult is false, the query would be:

SELECT name, id FROM my_table WHERE (name = 'youth' AND age < 21) LIMIT 10

If all values are false, the query would be:

SELECT * FROM my_table LIMIT 10

Documentation

Overview

Lightweight and easy to use query builder that works with any SQL dialect.

Index

Constants

This section is empty.

Variables

View Source
var (
	// SQL is the default dialect.
	// Uses '?' placeholders, expands arrays.
	SQL = &Dialect{
		Placeholder: func(w *bytes.Buffer, i int) {
			w.WriteByte('?')
		},
		ExpandArrays: true,
	}

	// SQLite is the dialect for SQLite.
	// Uses '?NNN' placeholders, expands arrays.
	SQLite = &Dialect{
		Placeholder: func(w *bytes.Buffer, i int) {
			w.WriteByte('?')
			w.WriteString(strconv.Itoa(i))
		},
		ExpandArrays: true,
	}

	// PostgreSQL is the dialect for PostgreSQL.
	// Uses '$NNN' placeholders, doesn't expand arrays.
	// Transforms [InValues] into "= ANY" and "!= ALL".
	PostgreSQL = &Dialect{
		Placeholder: func(w *bytes.Buffer, i int) {
			w.WriteByte('$')
			w.WriteString(strconv.Itoa(i))
		},
		ExpandArrays: false,
	}
)

Functions

This section is empty.

Types

type Dialect

type Dialect struct {
	Placeholder  func(w *bytes.Buffer, i int)
	ExpandArrays bool
}

Dialect represents an SQL dialect.

type Embedded

type Embedded string

Embedded is a string type that is directly embedded into the query. Note: Like Embedder, this is not to be used for untrusted input.

type Embedder

type Embedder interface {
	RawValue() string
}

Embedder embeds a value directly into a query string. Note: Since this is embedded and not bound, attention must be paid to sanitizing this input.

type Folded added in v0.3.0

type Folded[E any] []E

Folded is a type that tells bqb NOT to expand an array into individual parameters. The array won't be expanded regardless of the ExpandArray setting in a Dialect.

func Fold added in v0.3.0

func Fold[S ~[]E, E any](s S) Folded[E]

Fold converts a slice to Folded.

type JSONList

type JSONList []any

JSONList is a type that tells bqb to convert the parameter to a JSON list without requiring reflection.

type JSONMap

type JSONMap map[string]any

JSONMap is a type that tells bqb to convert the parameter to a JSON object without requiring reflection.

type Pool added in v0.5.0

type Pool struct {
	// contains filtered or unexported fields
}

Pool provides a pool of queries that may help reduce memory allocations and GC overhead. Queries creates by the pool will be automatically put into the pool after Query.Build is called and must not be used after the call.

func (*Pool) New added in v0.5.0

func (p *Pool) New(text string, params ...any) *Query

New returns a new Query initialized with text and params.

func (*Pool) Optional added in v0.5.0

func (p *Pool) Optional(prefix string) *Query

Optional returns a new Query that has a conditional prefix which only resolves when at least something is subsequently added to the query.

func (*Pool) Q added in v0.5.0

func (p *Pool) Q() *Query

Q returns a new empty Query.

type Query

type Query struct {
	// contains filtered or unexported fields
}

Query is an SQL query builder.

func New

func New(text string, params ...any) *Query

New returns a new Query initialized with text and params.

func Optional

func Optional(prefix string) *Query

Optional returns a new Query that has a conditional prefix which only resolves when at least something is subsequently added to the query.

func Q

func Q() *Query

Q returns a new empty Query.

func (*Query) And

func (q *Query) And(text string, args ...any) *Query

And appends SQL text and params to the query, joining with " AND " if the query already contains something.

func (*Query) Build

func (q *Query) Build(dialect *Dialect) (stmt string, args []any, err error)

Build builds the query into an SQL string and bound args using the given dialect.

func (*Query) Comma

func (q *Query) Comma(text string, args ...any) *Query

Comma appends SQL text and params to the query, joining with comma if the query already contains something.

func (*Query) Concat

func (q *Query) Concat(text string, args ...any) *Query

Concat appends SQL text and params to the query.

func (*Query) Empty

func (q *Query) Empty() bool

Empty returns true if the query is nil or empty.

func (*Query) Join

func (q *Query) Join(sep, text string, args ...any) *Query

Join appends SQL text and params to the query, joining with sep if the query already contains something.

func (*Query) Or

func (q *Query) Or(text string, args ...any) *Query

Or appends SQL text and params to the query, joining with " OR " if the query already contains something.

func (*Query) Space

func (q *Query) Space(text string, args ...any) *Query

Space appends SQL text and params to the query, joining with space if the query already contains something.

func (*Query) ToPgSQL

func (q *Query) ToPgSQL() (stmt string, args []any, err error)

ToPgSQL builds the query into an SQL string and bound args using PostgreSQL dialect.

func (*Query) ToSQL

func (q *Query) ToSQL() (stmt string, args []any, err error)

ToSQL builds the query into an SQL string and bound args using SQL dialect.

func (*Query) ToSQLite

func (q *Query) ToSQLite() (stmt string, args []any, err error)

ToSQLite builds the query into an SQL string and bound args using SQLite dialect.

type Unfolded added in v0.3.0

type Unfolded[E any] []E

Unfolded is a type that tells bqb to expand an array into individual parameters. The array will be expanded regardless of the ExpandArray setting in a Dialect.

func Unfold added in v0.3.0

func Unfold[S ~[]E, E any](s S) Unfolded[E]

Unfold converts a slice to Unfolded.

Jump to

Keyboard shortcuts

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