sqlb

package module
v0.7.4 Latest Latest
Warning

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

Go to latest
Published: Nov 28, 2023 License: Unlicense Imports: 15 Imported by: 1

README

Overview

sqlb: SQL Builder for Go. Features:

  • Supports plain SQL queries with ordinal or named params.
    • Supports argument lists.
    • Supports argument maps.
    • Supports argument structs.
  • Supports generating SQL clauses from structs.
    • Generate "select" clauses from structs.
    • Generate "insert" clauses from structs.
    • Generate "update" clauses from structs.
    • Generate "delete" clauses from structs.
    • Generate "and" and "or" conditional clauses from structs.
  • Provides data structures forming an SQL DSL in Go.
    • Shortcuts for common queries such as select, insert, update, delete.
    • Arbitrarily composable and nestable.
    • Uses data literals, not a builder API.
  • Supports an optional "JSON Expression Language" (JEL) for expressing SQL expressions with nested Lisp-style calls in JSON.
  • Supports safely parsing "order by" clauses from JSON and text, for specific struct types, converting field names from "json" field tags to "db" field tags.
  • Supports "sparse" structs, where not all fields are "present", allowing to implement HTTP PATCH semantics without sacrificing static typing.
  • Compatible with standard SQL syntax, biased towards Postgres.
  • Decently optimized.
  • Small and dependency-free.

API docs: https://pkg.go.dev/github.com/mitranim/sqlb.

See the sibling library https://github.com/mitranim/gos for scanning SQL rows into structs.

Examples

All examples imply the following import:

import s "github.com/mitranim/sqlb"

Query with named parameters and structs

func ExampleStrQ_structs() {
  type Output struct {
    Col0 string `db:"col0"`
    Col1 string `db:"col1"`
  }

  type Filter struct {
    Col2 int64 `db:"col2"`
    Col3 int64 `db:"col3"`
  }

  fmt.Println(s.Reify(
    s.StrQ{`
      select :cols from some_table where :filter
    `, s.Dict{
      `cols`:   s.Cols{(*Output)(nil)},
      `filter`: s.And{Filter{10, 20}},
    }},
  ))
  // Output:
  // select "col0", "col1" from some_table where "col2" = $1 and "col3" = $2 [10 20]
}

AST-style query building

func ExampleInsert_nonEmpty() {
  type Fields struct {
    Col0 int64 `db:"col0"`
    Col1 int64 `db:"col1"`
  }

  fmt.Println(s.Reify(s.Insert{`some_table`, Fields{10, 20}}))

  // Output:
  // insert into "some_table" ("col0", "col1") values ($1, $2) returning * [10 20]
}

Composition

func Example_composition() {
  inner := s.StrQ{
    `select * from some_table where col0 = :val`,
    s.Dict{`val`: 10},
  }

  outer := s.StrQ{
    `select * from (:inner) as _ where col1 = :val`,
    s.Dict{`inner`: inner, `val`: 20},
  }

  fmt.Println(s.Reify(outer))
  // Output:
  // select * from (select * from some_table where col0 = $1) as _ where col1 = $2 [10 20]
}

Changelog

v0.7.4

Minor fix for reporting types in error messages. Some internal tweaks.

v0.7.3

Added UpsertConflictVoid, UpsertConflict.

v0.7.2

Added ValidateUnusedArguments that globally disables or enables argument validation.

Internal improvements in errors.

v0.7.1

Added InsertVoid, UpdateVoid, DeleteVoid, UpsertVoid.

v0.7.0

Renamed method .Append in various types to .AppendTo for consistency with other libraries.

Renamed interface Appender to AppenderTo.

v0.6.8

Added LaxDict: dictionary of named arguments similar to Dict, but without support for validating unused arguments.

v0.6.7

Fixed an edge case bug in Upsert.

v0.6.6

Fixed support for non-empty sparse structs in Upsert.

v0.6.5

Add Upsert.

v0.6.4

OrdsParser support default "dir" and "nulls" in struct tags:

type Ordable struct {
  Name string `json:"name" db:"name" ord.dir:"desc" ord.nulls:"last"`
}

v0.6.3

  • Dir supports text parsing and encoding.
  • Minor breaking change: Bui.Arg now appends both an argument and a parameter.

v0.6.2

CommaAppender and ArrayAppender are now parametrized on the element type.

v0.6.1

Added StructsInsert, StructsInsertOf.

v0.6.0

  • Support role:"ref" struct field annotation for pointer-like generic types.

  • Require Go 1.18.

v0.5.3

Added SliceCommaAppender.

v0.5.2

Update now defaults to where null rather than where true. The new behavior is aligned with Delete.

Embedded struct fields tagged with db:"" or db:"-" are now completely ignored. The old behavior treated them as untagged, traversing them. The new behavior allows to completely skip an embedded struct, on an opt-in basis, without having to modify the db tags of its inner fields.

v0.5.1

Added optional support for a hidden Nullable interface, shared with the library github.com/mitranim/gt.

v0.5.0

  • Revised ords parsing.
    • Split OrdsParser into two types:
      • ParserOrds → suitable for inclusion into other types.
      • OrdsParser → suitable for transient, stackframe-local use.
    • Added (*Ords).OrdsParser suitable for use by types that embed Ords and implement custom parsing methods.
    • Support arbitrary filters for struct fields.
    • Added TagFilter for field filtering.
  • Renamed Sparse.HasField to Sparse.AllowField. Filter uses a method with the same signature, and the old name didn't make sense for it.
  • Misc:
    • Renamed (*Ords).AppendVals(*Ords).Add.
    • Added (*Ords).Zero.
    • Replaced Ords.Grow with (*Ords).Grow that modifies the receiver.

v0.4.3

Add SelectCount.

v0.4.2

Add Limit, Offset, LimitUint, OffsetUint.

v0.4.1

Exported ErrEmptyAssign used by StructAssign and Update to indicate the inability to generate a valid SQL "update set" clause.

v0.4.0

Added low-level tools for text encoding and SQL array encoding:

  • ArrayAppender
  • CommaAppender
  • String
  • TryString
  • Append
  • TryAppend
  • TryAppendWith
  • AppendWith
  • AppenderString

Breaking changes:

  • Removed useless expression type Int.
  • Renamed Bui.TryExprs to Bui.CatchExprs.

v0.3.0

Revised AST-style expressions:

  • Removed uselessly low-level exprs such as Space, ReturningStar, and so on.
  • Added higher-level shortcuts for extremely common yet simple operations:
    • Select
    • Insert
    • Update
    • Delete

v0.2.1

Added Sparse and Partial to support "sparse" structs, allowing to implement HTTP PATCH semantics more easily, efficiently, and correctly.

v0.2.0

Full API revision. Added many AST/DSL-like types for common expressions. Optimized parsing and expression building. Use caching and pooling to minimize redundant work. String-based query building now uses partial parsing with caching, and should no longer be a measurable expense. Ported JEL support from github.com/mitranim/jel.

v0.1.17

Added Ords.OrType.

v0.1.16

Added NamedArg.Norm. Improved NamedArg.IsNil and NamedArgs.Conditions. They now use the driver.Valuer.Value method, if present, to determine null-ness, which works for non-pointer "nullable" types.

v0.1.15

Ords.Or is now a value method that returns a modified version, rather than a pointer method that mutated the original.

v0.1.14

StructMap and StructNamedArgs now tolerate nil inputs. Previously, they tolerated non-nil interfaces where the underlying value was a nil struct pointer. Now they also allow nil interfaces.

v0.1.13

Fixed the bug where the Ords.Lax mode was appending malformed ords, instead of skipping them entirely.

v0.1.12

  • StrQuery now interpolates directly, without invoking (*Query).Append on the provided query. This allows to interpolate StrQuery strings that contain parameter placeholders. Use at your own risk.

  • (*Query).Append no longer has an argument length limit.

v0.1.11

Added Ords.Lax: a boolean that causes Ords to skip unknown fields during parsing.

v0.1.10

Breaking changes in the name of efficiency:

  • NamedArgs.Conditions now uses = and is null, as appropriate, instead of previous is not distinct from. At the time of writing, Postgres (version <= 12) is unable to use indexes for is not distinct from, which may result in much slower queries. The new approach avoids this gotcha.

  • In Ord, nulls last is now opt-in rather than default. In addition, asc/desc in input strings is now optional. This more precisely reflects SQL semantics and allows finer-grained control. More importantly, it avoids a potential performance gotcha. At the time of writing, Postgres (version <= 12) is unable to use normal indexes for nulls last ordering. Instead it requires specialized indexes where nulls last is set explicitly. Making it opt-in reduces the chance of accidental slowness.

    • Added OrdAscNl and OrdDescNl for convenient construction.

    • Minor breaking change: Ord.IsDesc is now Ord.Desc.

  • Minor breaking change: removed Ord.IsValid.

Non-breaking additions:

  • Ords.RowNumber(): generates a Postgres window function expression row_number() over (order by ...), falling back on a constant value when the ordering is empty.

  • QueryOrd(): shortcut for making a Query with a single .Append() invocation.

  • QueryNamed(): shortcut for making a Query with a single .AppendNamed() invocation.

0.1.9

Added Ords and Ord: structured representation of order by, able to decode from external input such as JSON, but also flexible enough to store arbitrary sub-queries. Ported from github.com/mitranim/jel, while also adding the ability to store sub-queries rather than only identifiers.

0.1.8

Added StrQuery.

0.1.7

Corrected CheckUnused to be true by default, which was always intended.

0.1.6

Added CheckUnused which allows to opt out of unused parameter checks in Query.Append and Query.AppendNamed. Can be convenient for development.

0.1.5

Minor bugfix: Query.String is now implemented on the non-pointer type, as intended. Also updated the sqlp dependency.

0.1.4

Breaking changes in Query: simpler interface, better performance.

Instead of storing and operating on a parsed AST, Query now stores the query text as []byte. We use sqlp.Tokenizer to parse inputs without generating an AST, transcoding parameters on the fly. IQuery now simply appends to an externally-passed Query, instead of having to return a parsed AST representation. All together, this significantly simplifies the implementation of Query and any external IQuery types.

0.1.3

Added Query.Clear().

0.1.2

Breaking: methods of NamedArgs now return queries, suitable for inclusion into other queries. Separate methods for strings and arg slices have been removed.

0.1.1

Dependency update.

0.1.0

First tagged release.

License

https://unlicense.org

Misc

I'm receptive to suggestions. If this library almost satisfies you but needs changes, open an issue or chat me up. Contacts: https://mitranim.com/#contacts

Documentation

Overview

Example (Composition)

Copy of `ExampleStrQ_nested` for package-level docs.

package main

import (
	"fmt"

	s "github.com/mitranim/sqlb"
)

func main() {
	inner := s.StrQ{
		`select * from some_table where col0 = :val`,
		s.Dict{`val`: 10},
	}

	outer := s.StrQ{
		`select * from (:inner) as _ where col1 = :val`,
		s.Dict{`inner`: inner, `val`: 20},
	}

	fmt.Println(s.Reify(outer))
}
Output:

select * from (select * from some_table where col0 = $1) as _ where col1 = $2 [10 20]

Index

Examples

Constants

View Source
const (
	TagNameDb   = `db`
	TagNameJson = `json`
)

Variables

View Source
var ErrEmptyAssign = error(ErrEmptyExpr{Err{
	`building SQL assignment expression`,
	ErrStr(`assignment must have at least one field`),
}})

Used by `StructAssign` to indicate that no fields were provided, and therefore it was unable to generate valid SQL for an "update set" clause. This can happen because the input struct was missing or empty, or because all fields were excluded through the use of `Sparse`. User code should detect this error to skip the DB request altogether.

View Source
var Ops = map[string]Op{
	`and`:                  OpInfix,
	`or`:                   OpInfix,
	`not`:                  OpPrefix,
	`is null`:              OpPostfix,
	`is not null`:          OpPostfix,
	`is true`:              OpPostfix,
	`is not true`:          OpPostfix,
	`is false`:             OpPostfix,
	`is not false`:         OpPostfix,
	`is unknown`:           OpPostfix,
	`is not unknown`:       OpPostfix,
	`is distinct from`:     OpInfix,
	`is not distinct from`: OpInfix,
	`=`:                    OpInfix,
	`~`:                    OpInfix,
	`~*`:                   OpInfix,
	`~=`:                   OpInfix,
	`<>`:                   OpInfix,
	`<`:                    OpInfix,
	`>`:                    OpInfix,
	`>=`:                   OpInfix,
	`<=`:                   OpInfix,
	`@@`:                   OpInfix,
	`any`:                  OpAny,
	`between`:              OpBetween,
}

Known SQL operations used in JEL. Serves as a whitelist, allowing us to differentiate them from casts, and describes how to transform JEL Lisp-style calls into SQL expressions (prefix, infix, etc.). This is case-sensitive and whitespace-sensitive.

View Source
var ValidateUnusedArguments = true

Enables validation of unused arguments in parametrized queries generated via `ListQ` / `DictQ` / `StructQ` / `StrQ` / `Preparse` / `Prep`. By default, validation is enabled. It can be disabled in two ways: globally by changing this variable to `false`, or by using an argument dictionary without support for argument validation, such as `LaxDict`.

Functions

func AppendTo added in v0.7.0

func AppendTo(buf []byte, src any) ([]byte, error)

Missing feature of the standard library: append the text representation of an arbitrary value to the buffer, prioritizing "append"-style encoding functions over "string"-style functions for efficiency, using only "intentional" representations, and without swallowing errors.

Supports ONLY the following types, in this order of priority. For other types, returns an error.

  • `AppenderTo`
  • `encoding.TextMarshaler`
  • `fmt.Stringer`
  • Built-in primitive types.
  • Aliases of `[]byte`.

Special cases:

  • Nil: append nothing, return buffer as-is.
  • Integers: use `strconv.AppendInt` in base 10.
  • Floats: use `strconv.AppendFloat` without scientific notation.

Used internally by `CommaAppender`, exported for advanced users.

func AppendWith added in v0.4.0

func AppendWith(buf *[]byte, delim string, val any) (bool, error)

Attempts to append the given delimiter and the text representation of the given value, via `AppendTo`. If after delimiter non-zero amount of bytes was appended, returns true. Otherwise reverts the buffer to the original length and returns false. If the buffer got reallocated with increased capacity, preserves the new capacity.

func AppenderString added in v0.4.0

func AppenderString(val AppenderTo) string

Tiny shortcut for encoding an `AppenderTo` implementation to a string by using its `.AppendTo` method, without paying for a string-to-byte conversion. Used internally by many `Expr` implementations. Exported because it's handy for defining new types.

func FieldDbName added in v0.2.0

func FieldDbName(field r.StructField) string

Returns the field's DB column name from the "db" tag, following the JSON convention of eliding anything after a comma and treating "-" as a non-name.

func FieldJsonName added in v0.2.0

func FieldJsonName(field r.StructField) string

Returns the field's JSON column name from the "json" tag, following the same conventions as the `encoding/json` package.

func Reify added in v0.2.0

func Reify(vals ...Expr) (string, []any)

Encodes the provided expressions and returns the resulting text and args. Shortcut for using `(*Bui).Exprs` and `Bui.Reify`. Provided mostly for examples. Actual code may want to use `Bui`:

bui := MakeBui(4096, 64)
panic(bui.CatchExprs(someExprs...))
text, args := bui.Reify()

func String added in v0.4.0

func String(src any) (string, error)

Missing feature of the standard library: return a string representation of an arbitrary value intended only for machine use, only for "intentionally" encodable types, without swallowing errors. Differences from `fmt.Sprint`:

  • Nil input = "" output.

  • Returns errors separately, without encoding them into the output. This is important when the output is intended to be passed to another system rather than read by humans.

  • Supports ONLY the following types, in this order of priority. For other types, returns an error.

  • `fmt.Stringer`

  • `AppenderTo`

  • `encoding.TextMarshaler`

  • Built-in primitive types.

  • Encodes floats without the scientific notation.

  • Aliases of `[]byte`.

func TryAppend added in v0.4.0

func TryAppend(buf []byte, src any) []byte

Variant of `AppendTo` that panics on error.

func TryAppendWith added in v0.4.0

func TryAppendWith(buf *[]byte, delim string, val any) bool

Variant of `AppendWith` that panics on error.

func TryString added in v0.4.0

func TryString(val any) string

Variant of `String` that panics on error.

func TypeCols added in v0.2.0

func TypeCols(typ r.Type) string

Returns the output of `Cols` for the given type, but takes `reflect.Type` as input, rather than a type-carrying `any`. Used internally by `Cols`. The result is cached and reused. Subsequent calls for the same type are nearly free.

func TypeColsDeep added in v0.2.0

func TypeColsDeep(typ r.Type) string

Returns the output of `ColsDeep` for the given type, but takes `reflect.Type` as input, rather than a type-carrying `any`. Used internally by `ColsDeep`. The result is cached and reused. Subsequent calls for the same type are nearly free.

Types

type AliasedPath added in v0.2.0

type AliasedPath []string

Represents an arbitrarily-nested SQL path that gets encoded as `Path` followed by `PseudoPath` alias. Useful for building "select" clauses. Used internally by `ColsDeep`.

Example
package main

import (
	"fmt"

	s "github.com/mitranim/sqlb"
)

func main() {
	fmt.Println(s.AliasedPath{`one`})
	fmt.Println(s.AliasedPath{`one`, `two`})
	fmt.Println(s.AliasedPath{`one`, `two`, `three`})
}
Output:

"one"
("one")."two" as "one.two"
("one")."two"."three" as "one.two.three"

func (AliasedPath) AppendExpr added in v0.2.0

func (self AliasedPath) AppendExpr(text []byte, args []any) ([]byte, []any)

Implement the `Expr` interface, making this a sub-expression.

func (AliasedPath) AppendTo added in v0.7.0

func (self AliasedPath) AppendTo(text []byte) []byte

Implement the `AppenderTo` interface, sometimes allowing more efficient text encoding.

func (AliasedPath) Norm added in v0.2.0

func (self AliasedPath) Norm() Expr

Normalizes the expression, returning nil or a single `Ident` if the length allows this. Otherwise returns self as-is.

func (AliasedPath) String added in v0.2.0

func (self AliasedPath) String() string

Implement the `fmt.Stringer` interface for debug purposes.

type And added in v0.2.0

type And [1]any

Represents a sequence of arbitrary sub-expressions or arguments joined by the SQL `and` operator. Rules for the inner value:

  • nil or empty -> fallback to `true`
  • single `Expr` -> render it as-is
  • non-empty slice -> render its individual elements joined by `and`
  • non-empty struct -> render column equality conditions joined by `and`
Example (Slice)
package main

import (
	"fmt"

	s "github.com/mitranim/sqlb"
)

func main() {
	type list = []any

	fmt.Println(s.Reify(s.And{nil}))
	fmt.Println(s.Reify(s.And{list{}}))
	fmt.Println(s.Reify(s.And{list{true, false, s.Ident(`some_col`)}}))

}
Output:

true []
true []
$1 and $2 and ("some_col") [true false]
Example (Struct)
package main

import (
	"fmt"

	s "github.com/mitranim/sqlb"
)

func main() {
	fmt.Println(s.Reify(s.And{struct{}{}}))

	fmt.Println(s.Reify(s.And{struct {
		Col0 bool `db:"col0"`
		Col1 any  `db:"col1"`
		Col2 any  `db:"col2"`
	}{
		true,
		nil,
		s.Call{`some_func`, []int{10}},
	}}))

}
Output:

true []
"col0" = $1 and "col1" is null and "col2" = (some_func ($2)) [true 10]

func (And) AppendExpr added in v0.2.0

func (self And) AppendExpr(text []byte, args []any) ([]byte, []any)

Implement the `Expr` interface, making this a sub-expression.

func (And) AppendTo added in v0.7.0

func (self And) AppendTo(text []byte) []byte

Implement the `AppenderTo` interface, sometimes allowing more efficient text encoding.

func (And) String added in v0.2.0

func (self And) String() string

Implement the `fmt.Stringer` interface for debug purposes.

type Ands added in v0.2.0

type Ands []any

Syntactic shortcut, same as `And` with a slice of sub-expressions or arguments.

Example
package main

import (
	"fmt"

	s "github.com/mitranim/sqlb"
)

func main() {
	fmt.Println(s.Reify(s.Ands{}))
	fmt.Println(s.Reify(s.Ands{true, false, s.Ident(`some_col`)}))
}
Output:

true []
$1 and $2 and ("some_col") [true false]

func (Ands) AppendExpr added in v0.2.0

func (self Ands) AppendExpr(text []byte, args []any) ([]byte, []any)

Implement the `Expr` interface, making this a sub-expression.

func (Ands) AppendTo added in v0.7.0

func (self Ands) AppendTo(text []byte) []byte

Implement the `AppenderTo` interface, sometimes allowing more efficient text encoding.

func (Ands) String added in v0.2.0

func (self Ands) String() string

Implement the `fmt.Stringer` interface for debug purposes.

type Any added in v0.2.0

type Any [1]any

Represents an SQL "any()" expression. The inner value may be an instance of `Expr`, or an arbitrary argument.

Example
package main

import (
	"fmt"

	s "github.com/mitranim/sqlb"
)

func main() {
	fmt.Println(s.Reify(s.Any{}))
	fmt.Println(s.Reify(s.Any{[]int{10, 20}}))
	fmt.Println(s.Reify(s.Any{s.Table{`some_table`}}))

}
Output:

any ($1) [<nil>]
any ($1) [[10 20]]
any (table "some_table") []

func (Any) AppendExpr added in v0.2.0

func (self Any) AppendExpr(text []byte, args []any) ([]byte, []any)

Implement the `Expr` interface, making this a sub-expression.

func (Any) AppendTo added in v0.7.0

func (self Any) AppendTo(text []byte) []byte

Implement the `AppenderTo` interface, sometimes allowing more efficient text encoding.

func (Any) String added in v0.2.0

func (self Any) String() string

Implement the `fmt.Stringer` interface for debug purposes.

type AppenderTo added in v0.7.0

type AppenderTo interface{ AppendTo([]byte) []byte }

Appends a text repesentation. Sometimes allows better efficiency than `fmt.Stringer`. Implemented by all `Expr` types in this package.

type ArgDict added in v0.2.0

type ArgDict interface {
	IsEmpty() bool
	Len() int
	GotOrdinal(int) (any, bool)
	GotNamed(string) (any, bool)
}

Dictionary of arbitrary arguments, ordinal and/or named. Used as input to `ParamExpr` (parametrized expressions). This package provides multiple implementations: slice-based `List`, map-based `Dict`, and struct-based `StructDict`. May optionally implement `OrdinalRanger` and `NamedRanger` to validate used/unused arguments.

type ArrayAppender added in v0.4.0

type ArrayAppender[A AppenderTo] []A

Intermediary tool for implementing SQL array encoding. Has the same behavior as `CommaAppender`, but the text output is always enclosed in `{}`.

func (ArrayAppender[A]) AppendTo added in v0.7.0

func (self ArrayAppender[A]) AppendTo(buf []byte) []byte

Implement `AppenderTo`. Same as `CommaAppender.AppendTo`, but the output is always enclosed in `{}`.

func (ArrayAppender[_]) Get added in v0.4.0

func (self ArrayAppender[_]) Get() any

func (ArrayAppender[_]) String added in v0.4.0

func (self ArrayAppender[_]) String() string

Implement `fmt.Stringer`. Same as `CommaAppender.String`, but the output is always enclosed in `{}`.

func (ArrayAppender[_]) Value added in v0.4.0

func (self ArrayAppender[_]) Value() (driver.Value, error)

type Assign added in v0.2.0

type Assign struct {
	Lhs Ident
	Rhs any
}

Represents an SQL assignment such as `"some_col" = arbitrary_expression`. The LHS must be a column name, while the RHS can be an `Expr` instance or an arbitrary argument.

Example
package main

import (
	"fmt"

	s "github.com/mitranim/sqlb"
)

func main() {
	fmt.Println(s.Reify(s.Assign{
		`some_col`,
		`arbitrary_value`,
	}))

	fmt.Println(s.Reify(s.Assign{
		`some_col`,
		s.Path{`some_table`, `another_col`},
	}))

}
Output:

"some_col" = $1 [arbitrary_value]
"some_col" = (("some_table")."another_col") []

func (Assign) AppendExpr added in v0.2.0

func (self Assign) AppendExpr(text []byte, args []any) ([]byte, []any)

Implement the `Expr` interface, making this a sub-expression.

func (Assign) AppendTo added in v0.7.0

func (self Assign) AppendTo(text []byte) []byte

Implement the `AppenderTo` interface, sometimes allowing more efficient text encoding.

func (Assign) String added in v0.2.0

func (self Assign) String() string

Implement the `fmt.Stringer` interface for debug purposes.

type Bui added in v0.2.0

type Bui struct {
	Text []byte
	Args []any
}

Short for "builder". Tiny shortcut for building SQL expressions. Significantly simplifies the code and avoids various common mistakes. Used internally by most `Expr` implementations in this package. Careful use of `Bui` incurs very litte overhead compared to writing the corresponding code inline. The design may allow future Go versions to optimize it away completely.

Example
package main

import (
	"fmt"

	s "github.com/mitranim/sqlb"
)

func main() {
	fmt.Println(s.Reify(SomeExpr{}))
}

type SomeExpr struct{}

func (self SomeExpr) AppendExpr(text []byte, args []any) ([]byte, []any) {
	bui := s.Bui{text, args}
	bui.Str(`select`)
	bui.Any(`some_value`)
	return bui.Get()
}
Output:

select $1 [some_value]

func MakeBui added in v0.2.0

func MakeBui(textCap, argsCap int) Bui

Prealloc tool. Makes a `Bui` with the specified capacity of the text and args buffers.

func (*Bui) Any added in v0.2.0

func (self *Bui) Any(val any)

Appends an arbitrary value. If the value implements `Expr`, this calls `(*Bui).Expr`, which may append to the text and args in arbitrary ways. Otherwise, appends an argument to the inner slice of args, and the corresponding ordinal parameter such as "$1"/"$2"/.../"$N" to the text.

func (*Bui) Arg added in v0.2.0

func (self *Bui) Arg(val any)

Appends an argument to `.Args` and a corresponding ordinal parameter to `.Text`.

func (*Bui) CatchExprs added in v0.4.0

func (self *Bui) CatchExprs(vals ...Expr) (err error)

Same as `(*Bui).Exprs` but catches panics. Since many functions in this package use panics, this should be used for final reification by apps that insist on errors-as-values.

Example
package main

import (
	"fmt"

	s "github.com/mitranim/sqlb"
)

func main() {
	bui := s.MakeBui(1024, 16)

	err := bui.CatchExprs(
		s.Select{`some_table`, s.Ands{true, false}},
	)
	if err != nil {
		panic(err)
	}

	text, args := bui.Reify()
	fmt.Println(text)
	fmt.Println(args)

}
Output:

select * from "some_table" where $1 and $2
[true false]

func (*Bui) Expr added in v0.2.0

func (self *Bui) Expr(val Expr)

Appends an expression, delimited from the preceding text by a space, if necessary. Nil input is a nop: nothing will be appended.

Should be used only if you already have an `Expr` value. If you have a concrete value that implements the interface, call `bui.Set(val.AppendExpr(bui.Get())` instead, to avoid a heap allocation and a minor slowdown.

func (*Bui) Exprs added in v0.2.0

func (self *Bui) Exprs(vals ...Expr)

Appends each expr by calling `(*Bui).Expr`. They will be space-separated as necessary.

func (Bui) Get added in v0.2.0

func (self Bui) Get() ([]byte, []any)

Returns text and args as-is. Useful shortcut for passing them to `AppendExpr`.

func (*Bui) Grow added in v0.2.0

func (self *Bui) Grow(textLen, argsLen int)

Increases the capacity (not length) of the text and args buffers by the specified amounts. If there's already enough capacity, avoids allocation.

func (*Bui) OrphanArg added in v0.6.3

func (self *Bui) OrphanArg(val any) OrdinalParam

Appends an arg to the inner slice of args, returning the corresponding ordinal parameter that should be appended to the text. Requires caution: does not append the corresponding ordinal parameter.

func (*Bui) OrphanParam added in v0.6.3

func (self *Bui) OrphanParam(val OrdinalParam)

Appends an ordinal parameter such as "$1", space-separated from previous text if necessary. Requires caution: does not verify the existence of the corresponding argument.

func (Bui) Reify added in v0.2.0

func (self Bui) Reify() (string, []any)

Shortcut for `self.String(), self.Args`. Go database drivers tend to require `string, []any` as inputs for queries and statements.

func (*Bui) Set added in v0.2.0

func (self *Bui) Set(text []byte, args []any)

Replaces text and args with the inputs. The following idiom is equivalent to `bui.Expr` but more efficient if the expression type is concrete, avoiding an interface-induced allocation:

bui.Set(SomeExpr{}.AppendExpr(bui.Get()))

func (*Bui) Space added in v0.2.0

func (self *Bui) Space()

Adds a space if the preceding text doesn't already end with a terminator.

func (*Bui) Str added in v0.2.0

func (self *Bui) Str(val string)

Appends the provided string, delimiting it from the previous text with a space if necessary.

func (Bui) String added in v0.2.0

func (self Bui) String() string

Returns inner text as a string, performing a free cast.

func (*Bui) SubAny added in v0.2.0

func (self *Bui) SubAny(val any)

Appends an arbitrary value or sub-expression. Like `(*Bui).Any`, but if the value implements `Expr`, this uses `(*Bui).SubExpr` in order to parenthesize the sub-expression.

func (*Bui) SubExpr added in v0.2.0

func (self *Bui) SubExpr(val Expr)

Appends a sub-expression wrapped in parens. Nil input is a nop: nothing will be appended.

Performance note: if you have a concrete value rather than an `Expr`, calling this method will allocate, so you may want to avoid it. If you already have an `Expr`, calling this is fine.

type Call added in v0.2.0

type Call struct {
	Text string
	Args any
}

Represents an SQL function call expression. The text prefix is optional and usually represents a function name. The args must be either nil, a single `Expr`, or a slice of arbitrary sub-expressions or arguments.

Example (Arguments)
package main

import (
	"fmt"

	s "github.com/mitranim/sqlb"
)

func main() {
	fmt.Println(s.Reify(s.Call{`some_func`, []int{10, 20, 30}}))
}
Output:

some_func ($1, $2, $3) [10 20 30]
Example (Empty)
package main

import (
	"fmt"

	s "github.com/mitranim/sqlb"
)

func main() {
	fmt.Println(s.Call{`some_func`, nil})
}
Output:

some_func ()
Example (SubExpression)
package main

import (
	"fmt"

	s "github.com/mitranim/sqlb"
)

func main() {
	fmt.Println(s.Call{`exists`, s.Table{`some_table`}})
}
Output:

exists (table "some_table")
Example (SubExpressions)
package main

import (
	"fmt"

	s "github.com/mitranim/sqlb"
)

func main() {
	fmt.Println(s.Call{`some_func`, []s.Ident{`one`, `two`}})
}
Output:

some_func (("one"), ("two"))

func (Call) AppendExpr added in v0.2.0

func (self Call) AppendExpr(text []byte, args []any) ([]byte, []any)

Implement the `Expr` interface, making this a sub-expression.

func (Call) AppendTo added in v0.7.0

func (self Call) AppendTo(text []byte) []byte

Implement the `AppenderTo` interface, sometimes allowing more efficient text encoding.

func (Call) String added in v0.2.0

func (self Call) String() string

Implement the `fmt.Stringer` interface for debug purposes.

type Cols

type Cols [1]any

Represents a column list for a "select" expression. The inner value may be of any type, and is used as a type carrier; its actual value is ignored. If the inner value is a struct or struct slice, the resulting expression is a list of column names corresponding to its fields, using a "db" tag. Otherwise the expression is `*`.

Unlike many other struct-scanning expressions, this doesn't support filtering via `Sparse`. It operates at the level of a struct type, not an individual struct value.

TODO actually support `Sparse` because it's used for insert.

Example (NonStruct)
package main

import (
	"fmt"

	s "github.com/mitranim/sqlb"
)

func main() {
	fmt.Println(s.Cols{})
	fmt.Println(s.Cols{(*int)(nil)})
	fmt.Println(s.Cols{(*[]string)(nil)})
}
Output:

*
*
*
Example (Struct)
package main

import (
	"fmt"

	s "github.com/mitranim/sqlb"
)

func main() {
	type Internal struct {
		Id   string `db:"id"`
		Name string `db:"name"`
	}

	type External struct {
		Id       string   `db:"id"`
		Name     string   `db:"name"`
		Internal Internal `db:"internal"`
	}

	fmt.Println(s.Cols{(*External)(nil)})
}
Output:

"id", "name", "internal"

func (Cols) AppendExpr added in v0.2.0

func (self Cols) AppendExpr(text []byte, args []any) ([]byte, []any)

Implement the `Expr` interface, making this a sub-expression.

func (Cols) AppendTo added in v0.7.0

func (self Cols) AppendTo(text []byte) []byte

Implement the `AppenderTo` interface, sometimes allowing more efficient text encoding.

func (Cols) String added in v0.2.0

func (self Cols) String() string

Implement the `fmt.Stringer` interface for debug purposes.

type ColsDeep added in v0.2.0

type ColsDeep [1]any

Represents a column list for a "select" expression. The inner value may be of any type, and is used as a type carrier; its actual value is ignored. If the inner value is a struct or struct slice, the resulting expression is a list of column names corresponding to its fields, using a "db" tag. Otherwise the expression is `*`.

Unlike `Cols`, this has special support for nested structs and nested column paths. See the examples.

Unlike many other struct-scanning expressions, this doesn't support filtering via `Sparse`. It operates at the level of a struct type, not an individual struct value.

Example (NonStruct)
package main

import (
	"fmt"

	s "github.com/mitranim/sqlb"
)

func main() {
	fmt.Println(s.ColsDeep{})
	fmt.Println(s.ColsDeep{(*int)(nil)})
	fmt.Println(s.ColsDeep{(*[]string)(nil)})
}
Output:

*
*
*
Example (Struct)
package main

import (
	"fmt"

	s "github.com/mitranim/sqlb"
)

func main() {
	type Internal struct {
		Id   string `db:"id"`
		Name string `db:"name"`
	}

	type External struct {
		Id       string   `db:"id"`
		Name     string   `db:"name"`
		Internal Internal `db:"internal"`
	}

	fmt.Println(s.ColsDeep{(*External)(nil)})
}
Output:

"id", "name", ("internal")."id" as "internal.id", ("internal")."name" as "internal.name"

func (ColsDeep) AppendExpr added in v0.2.0

func (self ColsDeep) AppendExpr(text []byte, args []any) ([]byte, []any)

Implement the `Expr` interface, making this a sub-expression.

func (ColsDeep) AppendTo added in v0.7.0

func (self ColsDeep) AppendTo(text []byte) []byte

Implement the `AppenderTo` interface, sometimes allowing more efficient text encoding.

func (ColsDeep) String added in v0.2.0

func (self ColsDeep) String() string

Implement the `fmt.Stringer` interface for debug purposes.

type Comma added in v0.2.0

type Comma [1]any

Represents a comma-separated list of arbitrary sub-expressions. The inner value may be nil or a single `Expr`, otherwise it must be a slice.

func (Comma) AppendExpr added in v0.2.0

func (self Comma) AppendExpr(text []byte, args []any) ([]byte, []any)

Implement the `Expr` interface, making this a sub-expression.

func (Comma) AppendTo added in v0.7.0

func (self Comma) AppendTo(text []byte) []byte

Implement the `AppenderTo` interface, sometimes allowing more efficient text encoding.

func (Comma) String added in v0.2.0

func (self Comma) String() string

Implement the `fmt.Stringer` interface for debug purposes.

type CommaAppender added in v0.4.0

type CommaAppender[A AppenderTo] []A

Intermediary tool for implementing SQL array encoding. Combines multiple arbitrary text encoders. On demand (on a call to `.AppendTo` or `.String`), combines their text representations, separating them with a comma, while skipping any empty representations. The output will never contain a dangling leading comma, double comma, or leading trailing comma, unless they were explicitly generated by the inner encoders. Compare `SliceCommaAppender` which takes an arbitrary slice.

func (CommaAppender[_]) AppendTo added in v0.7.0

func (self CommaAppender[_]) AppendTo(buf []byte) []byte

Implement `AppenderTo`. Appends comma-separated text representations of the inner encoders to the output buffer, skipping any empty representations.

func (CommaAppender[_]) String added in v0.4.0

func (self CommaAppender[_]) String() string

Implement `fmt.Stringer` by calling `.AppendTo`.

type Cond added in v0.2.0

type Cond Seq

Superset of `Seq` with additional support for structs. When the inner value is a struct, this generates a sequence of equality expressions, comparing the struct's column names against the corresponding field values. Field values may be arbitrary sub-expressions or arguments.

This is mostly an internal tool for building other expression types. Used internally by `And` and `Or`.

func (Cond) AppendExpr added in v0.2.0

func (self Cond) AppendExpr(text []byte, args []any) ([]byte, []any)

Implement the `Expr` interface, making this a sub-expression.

func (Cond) AppendTo added in v0.7.0

func (self Cond) AppendTo(text []byte) []byte

Implement the `AppenderTo` interface, sometimes allowing more efficient text encoding.

func (Cond) String added in v0.2.0

func (self Cond) String() string

Implement the `fmt.Stringer` interface for debug purposes.

type Delete added in v0.3.0

type Delete DeleteVoid

Shortcut for simple `delete from A where B returning *` expressions. See the examples. Also see `DeleteVoid` which doesn't have `returning *`.

Example (Filtered)
package main

import (
	"fmt"

	s "github.com/mitranim/sqlb"
)

func main() {
	type Filter struct {
		Col0 int64 `db:"col0"`
		Col1 int64 `db:"col1"`
	}

	fmt.Println(s.Reify(s.Delete{`some_table`, Filter{10, 20}}))

}
Output:

delete from "some_table" where "col0" = $1 and "col1" = $2 returning * [10 20]
Example (Unfiltered)
package main

import (
	"fmt"

	s "github.com/mitranim/sqlb"
)

func main() {
	fmt.Println(s.Reify(s.Delete{`some_table`, nil}))
}
Output:

delete from "some_table" where null returning * []

func (Delete) AppendExpr added in v0.3.0

func (self Delete) AppendExpr(text []byte, args []any) ([]byte, []any)

Implement the `Expr` interface, making this a sub-expression.

func (Delete) AppendTo added in v0.7.0

func (self Delete) AppendTo(text []byte) []byte

Implement the `AppenderTo` interface, sometimes allowing more efficient text encoding.

func (Delete) String added in v0.3.0

func (self Delete) String() string

Implement the `fmt.Stringer` interface for debug purposes.

type DeleteVoid added in v0.7.1

type DeleteVoid struct {
	From  Ident
	Where any
}

Shortcut for simple `delete from A where B` expressions. Also see `Delete` which appends `returning *`.

func (DeleteVoid) AppendExpr added in v0.7.1

func (self DeleteVoid) AppendExpr(text []byte, args []any) ([]byte, []any)

Implement the `Expr` interface, making this a sub-expression.

func (DeleteVoid) AppendTo added in v0.7.1

func (self DeleteVoid) AppendTo(text []byte) []byte

Implement the `AppenderTo` interface, sometimes allowing more efficient text encoding.

func (DeleteVoid) String added in v0.7.1

func (self DeleteVoid) String() string

Implement the `fmt.Stringer` interface for debug purposes.

type Dict added in v0.2.0

type Dict map[string]any

Variant of `map[string]any` conforming to the `ArgDict` interface. Supports only named parameters, not ordinal parameters. Used for `StrQ`. See the `DictQ` shortcut.

func (Dict) GotNamed added in v0.2.0

func (self Dict) GotNamed(key string) (any, bool)

Implement part of the `ArgDict` interface.

func (Dict) GotOrdinal added in v0.2.0

func (self Dict) GotOrdinal(int) (any, bool)

Implement part of the `ArgDict` interface. Always returns `nil, false`.

func (Dict) IsEmpty added in v0.2.0

func (self Dict) IsEmpty() bool

Implement part of the `ArgDict` interface.

func (Dict) Len added in v0.2.0

func (self Dict) Len() int

Implement part of the `ArgDict` interface.

func (Dict) RangeNamed added in v0.2.0

func (self Dict) RangeNamed(fun func(string))

Implement `NamedRanger` to automatically validate used/unused arguments.

type Dir added in v0.2.0

type Dir byte

Short for "direction". Enum for ordering direction: none, "asc", "desc".

const (
	DirNone Dir = 0
	DirAsc  Dir = 1
	DirDesc Dir = 2
)

func (Dir) AppendTo added in v0.7.0

func (self Dir) AppendTo(text []byte) []byte

Implement the `AppenderTo` interface, sometimes allowing more efficient text encoding.

func (Dir) GoString added in v0.2.0

func (self Dir) GoString() string

Implement `fmt.GoStringer` for debug purposes. Returns valid Go code representing this value.

func (Dir) MarshalJSON added in v0.6.3

func (self Dir) MarshalJSON() ([]byte, error)

Implement `json.Marshaler`.

func (Dir) MarshalText added in v0.6.3

func (self Dir) MarshalText() ([]byte, error)

Implement `encoding.TextUnmarshaler`.

func (*Dir) Parse added in v0.6.3

func (self *Dir) Parse(src string) error

Parses from a string, which must be either empty, "asc" or "desc".

func (Dir) String added in v0.2.0

func (self Dir) String() string

Implement `fmt.Stringer` for debug purposes.

func (*Dir) UnmarshalText added in v0.6.3

func (self *Dir) UnmarshalText(src []byte) error

Implement `encoding.TextMarshaler`.

type Eq added in v0.2.0

type Eq [2]any

Short for "equal". Represents SQL equality such as `A = B` or `A is null`. Counterpart to `Neq`.

Example
package main

import (
	"fmt"

	s "github.com/mitranim/sqlb"
)

func main() {
	fmt.Println(s.Reify(s.Eq{10, 20}))

	fmt.Println(s.Reify(s.Eq{
		s.Ident(`some_col`),
		nil,
	}))

	fmt.Println(s.Reify(s.Eq{
		s.Ident(`some_col`),
		s.Ident(`another_col`),
	}))

	fmt.Println(s.Reify(s.Eq{
		s.Ident(`some_col`),
		s.Path{`some_table`, `another_col`},
	}))

}
Output:

$1 = $2 [10 20]
("some_col") is null []
("some_col") = ("another_col") []
("some_col") = (("some_table")."another_col") []

func (Eq) AppendExpr added in v0.2.0

func (self Eq) AppendExpr(text []byte, args []any) ([]byte, []any)

Implement the `Expr` interface, making this a sub-expression.

func (Eq) AppendLhs added in v0.2.0

func (self Eq) AppendLhs(text []byte, args []any) ([]byte, []any)

Note: LHS and RHS are encoded differently because some SQL equality expressions are asymmetric. For example, `any` allows an array only on the RHS, and there's no way to invert it (AFAIK).

func (Eq) AppendRhs added in v0.2.0

func (self Eq) AppendRhs(text []byte, args []any) ([]byte, []any)

func (Eq) AppendTo added in v0.7.0

func (self Eq) AppendTo(text []byte) []byte

Implement the `AppenderTo` interface, sometimes allowing more efficient text encoding.

func (Eq) String added in v0.2.0

func (self Eq) String() string

Implement the `fmt.Stringer` interface for debug purposes.

type EqAny added in v0.2.0

type EqAny [2]any

Represents an SQL expression `A = any(B)`. Counterpart to `NeqAny`.

Example
package main

import (
	"fmt"

	s "github.com/mitranim/sqlb"
)

func main() {
	fmt.Println(s.Reify(s.EqAny{
		10,
		[]int{20, 30},
	}))

	fmt.Println(s.Reify(s.EqAny{
		s.Ident(`some_col`),
		[]int{20, 30},
	}))

	fmt.Println(s.Reify(s.EqAny{
		s.Ident(`some_col`),
		s.Table{`some_table`},
	}))

}
Output:

$1 = any ($2) [10 [20 30]]
("some_col") = any ($1) [[20 30]]
("some_col") = any (table "some_table") []

func (EqAny) AppendExpr added in v0.2.0

func (self EqAny) AppendExpr(text []byte, args []any) ([]byte, []any)

Implement the `Expr` interface, making this a sub-expression.

func (EqAny) AppendTo added in v0.7.0

func (self EqAny) AppendTo(text []byte) []byte

Implement the `AppenderTo` interface, sometimes allowing more efficient text encoding.

func (EqAny) String added in v0.2.0

func (self EqAny) String() string

Implement the `fmt.Stringer` interface for debug purposes.

type Err

type Err struct {
	While string
	Cause error
}

All errors generated by this package have this type, usually wrapped into a more specialized one: `ErrInvalidInput{Err{...}}`.

func (Err) Error

func (self Err) Error() string

Implement the `error` interface.

func (Err) Format added in v0.7.2

func (self Err) Format(out fmt.State, verb rune)

Implement the `fmt.Formatter` interface. Similar to `Err.Error`, but when formatting `.Cause`, uses `%+v`. By convention used by various libraries, this may print the cause's stack trace, if any.

func (Err) Unwrap

func (self Err) Unwrap() error

Implement a hidden interface in "errors".

type ErrEmptyExpr added in v0.2.1

type ErrEmptyExpr struct{ Err }

Specialized type for errors reported by some functions.

func (ErrEmptyExpr) Error added in v0.2.1

func (self ErrEmptyExpr) Error() string

Implement the `error` interface.

func (ErrEmptyExpr) Format added in v0.7.2

func (self ErrEmptyExpr) Format(out fmt.State, verb rune)

Implement the `fmt.Formatter` interface.

type ErrInternal added in v0.1.11

type ErrInternal struct{ Err }

Specialized type for errors reported by some functions.

func (ErrInternal) Error added in v0.2.0

func (self ErrInternal) Error() string

Implement the `error` interface.

func (ErrInternal) Format added in v0.7.2

func (self ErrInternal) Format(out fmt.State, verb rune)

Implement the `fmt.Formatter` interface.

type ErrInvalidInput

type ErrInvalidInput struct{ Err }

Specialized type for errors reported by some functions.

func (ErrInvalidInput) Error added in v0.2.0

func (self ErrInvalidInput) Error() string

Implement the `error` interface.

func (ErrInvalidInput) Format added in v0.7.2

func (self ErrInvalidInput) Format(out fmt.State, verb rune)

Implement the `fmt.Formatter` interface.

type ErrMissingArgument

type ErrMissingArgument struct{ Err }

Specialized type for errors reported by some functions.

func (ErrMissingArgument) Error added in v0.2.0

func (self ErrMissingArgument) Error() string

Implement the `error` interface.

func (ErrMissingArgument) Format added in v0.7.2

func (self ErrMissingArgument) Format(out fmt.State, verb rune)

Implement the `fmt.Formatter` interface.

type ErrOrdinalOutOfBounds added in v0.1.6

type ErrOrdinalOutOfBounds struct{ Err }

Specialized type for errors reported by some functions.

func (ErrOrdinalOutOfBounds) Error added in v0.2.0

func (self ErrOrdinalOutOfBounds) Error() string

Implement the `error` interface.

func (ErrOrdinalOutOfBounds) Format added in v0.7.2

func (self ErrOrdinalOutOfBounds) Format(out fmt.State, verb rune)

Implement the `fmt.Formatter` interface.

type ErrStr added in v0.7.2

type ErrStr string

String typedef that implements `error`. Errors of this type can be defined as constants.

func (ErrStr) Error added in v0.7.2

func (self ErrStr) Error() string

Implement `error`.

func (ErrStr) String added in v0.7.2

func (self ErrStr) String() string

Implement `fmt.Stringer`.

type ErrUnexpectedEOF added in v0.2.0

type ErrUnexpectedEOF struct{ Err }

Specialized type for errors reported by some functions.

func (ErrUnexpectedEOF) Error added in v0.2.0

func (self ErrUnexpectedEOF) Error() string

Implement the `error` interface.

func (ErrUnexpectedEOF) Format added in v0.7.2

func (self ErrUnexpectedEOF) Format(out fmt.State, verb rune)

Implement the `fmt.Formatter` interface.

type ErrUnexpectedParameter

type ErrUnexpectedParameter struct{ Err }

Specialized type for errors reported by some functions.

func (ErrUnexpectedParameter) Error added in v0.2.0

func (self ErrUnexpectedParameter) Error() string

Implement the `error` interface.

func (ErrUnexpectedParameter) Format added in v0.7.2

func (self ErrUnexpectedParameter) Format(out fmt.State, verb rune)

Implement the `fmt.Formatter` interface.

type ErrUnknownField added in v0.1.11

type ErrUnknownField struct{ Err }

Specialized type for errors reported by some functions.

func (ErrUnknownField) Error added in v0.2.0

func (self ErrUnknownField) Error() string

Implement the `error` interface.

func (ErrUnknownField) Format added in v0.7.2

func (self ErrUnknownField) Format(out fmt.State, verb rune)

Implement the `fmt.Formatter` interface.

type ErrUnusedArgument

type ErrUnusedArgument struct{ Err }

Specialized type for errors reported by some functions.

func (ErrUnusedArgument) Error added in v0.2.0

func (self ErrUnusedArgument) Error() string

Implement the `error` interface.

func (ErrUnusedArgument) Format added in v0.7.2

func (self ErrUnusedArgument) Format(out fmt.State, verb rune)

Implement the `fmt.Formatter` interface.

type Expr added in v0.2.0

type Expr interface {
	AppendExpr([]byte, []any) ([]byte, []any)
}

Short for "expression". Defines an arbitrary SQL expression. The method appends arbitrary SQL text. In both the input and output, the arguments must correspond to the parameters in the SQL text. Different databases support different styles of ordinal parameters. This package always generates Postgres-style ordinal parameters such as "$1", renumerating them as necessary.

This method is allowed to panic. Use `(*Bui).CatchExprs` to catch expression-encoding panics and convert them to errors.

All `Expr` types in this package also implement `AppenderTo` and `fmt.Stringer`.

type Exprs added in v0.2.0

type Exprs []Expr

Variable-sized sequence of expressions. When encoding, expressions will be space-separated if necessary.

Example
package main

import (
	"fmt"

	s "github.com/mitranim/sqlb"
)

func main() {
	type Filter struct {
		Slug string `db:"slug"`
	}

	fmt.Println(s.Reify(
		s.Select{`some_table`, Filter{`some_slug`}},
	))
}
Output:

select * from "some_table" where "slug" = $1 [some_slug]

func (Exprs) AppendExpr added in v0.2.0

func (self Exprs) AppendExpr(text []byte, args []any) ([]byte, []any)

Implement the `Expr` interface, making this a sub-expression.

func (Exprs) AppendTo added in v0.7.0

func (self Exprs) AppendTo(text []byte) []byte

Implement the `AppenderTo` interface, sometimes allowing more efficient text encoding.

func (Exprs) String added in v0.2.0

func (self Exprs) String() string

Implement the `fmt.Stringer` interface for debug purposes.

type Filter added in v0.5.0

type Filter interface{ AllowField(r.StructField) bool }

Filters struct fields. Used by `Sparse` and `ParseOpt`. Implemented by `TagFilter`.

type Haser added in v0.2.1

type Haser interface{ Has(string) bool }

Used by `Partial` for filtering struct fields. See `Sparse` and `Partial` for explanations.

type Ident added in v0.2.0

type Ident string

Represents an SQL identifier, always quoted.

Example
package main

import (
	"fmt"

	s "github.com/mitranim/sqlb"
)

func main() {
	fmt.Println(s.Ident(``))
	fmt.Println(s.Ident(`one`))
}
Output:

""
"one"
Example (Interpolation)
package main

import (
	"fmt"

	s "github.com/mitranim/sqlb"
)

func main() {
	fmt.Println(s.Reify(
		s.StrQ{
			`select :col from some_table where :col <> :val`,
			s.Dict{
				`col`: s.Ident(`some_col`),
				`val`: `some_val`,
			},
		},
	))
}
Output:

select "some_col" from some_table where "some_col" <> $1 [some_val]

func (Ident) AppendExpr added in v0.2.0

func (self Ident) AppendExpr(text []byte, args []any) ([]byte, []any)

Implement the `Expr` interface, making this a sub-expression.

func (Ident) AppendTo added in v0.7.0

func (self Ident) AppendTo(text []byte) []byte

Implement the `AppenderTo` interface, sometimes allowing more efficient text encoding.

func (Ident) BuiAppend added in v0.6.5

func (self Ident) BuiAppend(bui *Bui)

Shortcut for internal use.

func (Ident) String added in v0.2.0

func (self Ident) String() string

Implement the `fmt.Stringer` interface for debug purposes.

type Identifier added in v0.2.0

type Identifier []string

Represents a nested SQL identifier where all elements are quoted but not parenthesized. Useful for schema-qualified paths. For nested paths that don't begin with a schema, use `Path` instead.

Example
package main

import (
	"fmt"

	s "github.com/mitranim/sqlb"
)

func main() {
	fmt.Println(s.Identifier{`one`})
	fmt.Println(s.Identifier{`one`, `two`})
	fmt.Println(s.Identifier{`one`, `two`, `three`})
}
Output:

"one"
"one"."two"
"one"."two"."three"

func (Identifier) AppendExpr added in v0.2.0

func (self Identifier) AppendExpr(text []byte, args []any) ([]byte, []any)

Implement the `Expr` interface, making this a sub-expression.

func (Identifier) AppendTo added in v0.7.0

func (self Identifier) AppendTo(text []byte) []byte

Implement the `AppenderTo` interface, sometimes allowing more efficient text encoding.

func (Identifier) Norm added in v0.2.0

func (self Identifier) Norm() Expr

Normalizes the expression, returning nil or a single `Ident` if the length allows this. Otherwise returns self as-is.

func (Identifier) String added in v0.2.0

func (self Identifier) String() string

Implement the `fmt.Stringer` interface for debug purposes.

type Insert added in v0.3.0

type Insert InsertVoid

Shortcut for simple `insert into A (B) values (C) returning *` expressions. See the examples. Also see `InsertVoid` which doesn't have `returning *`.

Example (Empty)
package main

import (
	"fmt"

	s "github.com/mitranim/sqlb"
)

func main() {
	fmt.Println(s.Reify(s.Insert{`some_table`, nil}))
}
Output:

insert into "some_table" default values returning * []
Example (NonEmpty)
package main

import (
	"fmt"

	s "github.com/mitranim/sqlb"
)

func main() {
	type Fields struct {
		Col0 int64 `db:"col0"`
		Col1 int64 `db:"col1"`
	}

	fmt.Println(s.Reify(s.Insert{`some_table`, Fields{10, 20}}))

}
Output:

insert into "some_table" ("col0", "col1") values ($1, $2) returning * [10 20]

func (Insert) AppendExpr added in v0.3.0

func (self Insert) AppendExpr(text []byte, args []any) ([]byte, []any)

Implement the `Expr` interface, making this a sub-expression.

func (Insert) AppendTo added in v0.7.0

func (self Insert) AppendTo(text []byte) []byte

Implement the `AppenderTo` interface, sometimes allowing more efficient text encoding.

func (Insert) String added in v0.3.0

func (self Insert) String() string

Implement the `fmt.Stringer` interface for debug purposes.

type InsertVoid added in v0.7.1

type InsertVoid struct {
	Into   Ident
	Fields any
}

Shortcut for simple `insert into A (B) values (C)` expressions. Also see `Insert` which appends `returning *`.

func (InsertVoid) AppendExpr added in v0.7.1

func (self InsertVoid) AppendExpr(text []byte, args []any) ([]byte, []any)

Implement the `Expr` interface, making this a sub-expression.

func (InsertVoid) AppendTo added in v0.7.1

func (self InsertVoid) AppendTo(text []byte) []byte

Implement the `AppenderTo` interface, sometimes allowing more efficient text encoding.

func (InsertVoid) String added in v0.7.1

func (self InsertVoid) String() string

Implement the `fmt.Stringer` interface for debug purposes.

type Jel added in v0.2.0

type Jel struct {
	Type r.Type
	Text string
}

Short for "JSON Expression Language". Provides support for expressing a whitelisted subset of SQL with JSON, as Lisp-style nested lists. Transcodes JSON to SQL on the fly. Implements `Expr`. Can be transparently used as a sub-expression in other `sqlb` expressions. See the provided example.

Expressions are Lisp-style, using nested lists to express "calls". This syntax is used for all SQL operations. Binary infix operators are considered variadic.

Lists are used for calls and casts. The first element must be a string. It may be one of the whitelisted operators or functions, listed in `Ops`. If not, it must be a field name or a dot-separated field path. Calls are arbitrarily nestable.

["and", true, ["or", true, ["and", true, false]]]

["<=", 10, 20]

["=", "someField", "otherField"]

["and",
	["=", "someField", "otherField"],
	["<=", "dateField", ["dateField", "9999-01-01T00:00:00Z"]]
]

Transcoding from JSON to SQL is done by consulting two things: the built-in whitelist of SQL operations (`Ops`, shared), and a struct type provided to that particular decoder. The struct serves as a whitelist of available identifiers, and allows to determine value types via casting.

Casting allows to decode arbitrary JSON directly into the corresponding Go type:

["someDateField", "9999-01-01T00:00:00Z"]

["someGeoField", {"lng": 10, "lat": 20}]

Such decoded values are substituted with ordinal parameters such as $1, and appended to the slice of arguments (see below).

A string not in a call position and not inside a cast is interpreted as an identifier: field name or nested field path, dot-separated. It must be found on the reference struct, otherwise transcoding fails with an error.

"someField"

"outerField.innerField"

Literal numbers, booleans, and nulls that occur outside of casts are decoded into their Go equivalents. Like casts, they're substituted with ordinal parameters and appended to the slice of arguments.

JSON queries are transcoded against a struct, by matching fields tagged with `json` against fields tagged with `db`. Literal values are JSON-decoded into the types of the corresponding struct fields.

type Input struct {
	FieldOne string `json:"fieldOne" db:"field_one"`
	FieldTwo struct {
		FieldThree *time.Time `json:"fieldThree" db:"field_three"`
	} `json:"fieldTwo" db:"field_two"`
}

const src = `
	["and",
		["=", "fieldOne", ["fieldOne", "literal string"]],
		["<", "fieldTwo.fieldThree", ["fieldTwo.fieldThree", "9999-01-01T00:00:00Z"]]
	]
`

expr := Jel{Type: reflect.TypeOf((*Input)(nil)).Elem(), Text: src}
text, args := Reify(expr)

The result is roughly equivalent to the following (formatted for clarity):

text := `
	"field_one" = 'literal string'
	and
	("field_two")."field_three" < '9999-01-01T00:00:00Z'
`
args := []any{"literal string", time.Time("9999-01-01T00:00:00Z")}
Example
package main

import (
	"fmt"
	r "reflect"
	"time"

	"github.com/mitranim/sqlb"
)

func main() {
	type Internal struct {
		InternalTime *time.Time `json:"internalTime" db:"internal_time"`
	}

	type External struct {
		ExternalName string   `json:"externalName" db:"external_name"`
		Internal     Internal `json:"internal"     db:"internal"`
	}

	const src = `
		["and",
			["or",
				false,
				["=", "externalName", ["externalName", "literal string"]]
			],
			["and",
				true,
				["<", "internal.internalTime", ["internal.internalTime", "9999-01-01T00:00:00Z"]]
			]
		]
	`

	expr := sqlb.Jel{
		Type: r.TypeOf((*External)(nil)).Elem(),
		Text: src,
	}

	text, args := sqlb.Reify(expr)

	fmt.Println(string(text))
	fmt.Printf("%#v\n", args)

}
Output:

(($1 or ("external_name" = $2)) and ($3 and (("internal")."internal_time" < $4)))
[]interface {}{false, "literal string", true, time.Date(9999, time.January, 1, 0, 0, 0, 0, time.UTC)}

func JelFor added in v0.2.0

func JelFor(typ any) Jel

Shortcut for instantiating `Jel` with the type of the given value. The input is used only as a type carrier.

func (Jel) AppendExpr added in v0.2.0

func (self Jel) AppendExpr(text []byte, args []any) ([]byte, []any)

Implement `Expr`, allowing this to be used as a sub-expression in queries built with "github.com/mitranim/sqlb". Always generates a valid boolean expression, falling back on "true" if empty.

func (Jel) AppendTo added in v0.7.0

func (self Jel) AppendTo(text []byte) []byte

Implement the `AppenderTo` interface, sometimes allowing more efficient text encoding.

func (*Jel) OrType added in v0.2.0

func (self *Jel) OrType(typ any)

If `.Type` is empty, sets the type of the provided value. Otherwise this is a nop. The input is used only as a type carrier; its actual value is ignored.

func (*Jel) Parse added in v0.2.0

func (self *Jel) Parse(val string) error

Stores the input for future use in `.AppendExpr`. Input must be valid JSON.

func (Jel) String added in v0.2.0

func (self Jel) String() string

Implement the `fmt.Stringer` interface for debug purposes.

func (*Jel) UnmarshalJSON added in v0.2.0

func (self *Jel) UnmarshalJSON(val []byte) error

Stores the input for future use in `.AppendExpr`. Input must be valid JSON.

func (*Jel) UnmarshalText added in v0.2.0

func (self *Jel) UnmarshalText(val []byte) error

Stores the input for future use in `.AppendExpr`. Input must be valid JSON.

type LaxDict added in v0.6.8

type LaxDict Dict

Variant of `Dict` without support for validating unused arguments. Note that missing arguments are still detected and cause errors. Useful when generating the dictionary dynamically, rather than hardcoding the set of keys. Must be used with `StrQ` or `Prep`, rather than with `DictQ`, because the latter always converts the given dictionary to `Dict`.

func (LaxDict) GotNamed added in v0.6.8

func (self LaxDict) GotNamed(key string) (any, bool)

Implement part of the `ArgDict` interface.

func (LaxDict) GotOrdinal added in v0.6.8

func (self LaxDict) GotOrdinal(int) (any, bool)

Implement part of the `ArgDict` interface. Always returns `nil, false`.

func (LaxDict) IsEmpty added in v0.6.8

func (self LaxDict) IsEmpty() bool

Implement part of the `ArgDict` interface.

func (LaxDict) Len added in v0.6.8

func (self LaxDict) Len() int

Implement part of the `ArgDict` interface.

type Limit added in v0.4.2

type Limit [1]any

Represents SQL expression "limit N" with an arbitrary argument or sub-expression. Implements `Expr`:

  • If nil -> append nothing.
  • If expr -> append "limit (<sub-expression>)".
  • If val -> append "limit $N" with the corresponding argument.

func (Limit) AppendExpr added in v0.4.2

func (self Limit) AppendExpr(text []byte, args []any) ([]byte, []any)

Implement the `Expr` interface, making this a sub-expression.

func (Limit) AppendTo added in v0.7.0

func (self Limit) AppendTo(text []byte) []byte

Implement the `AppenderTo` interface, sometimes allowing more efficient text encoding.

func (Limit) String added in v0.4.2

func (self Limit) String() string

Implement the `fmt.Stringer` interface for debug purposes.

type LimitUint added in v0.4.2

type LimitUint uint64

Represents SQL expression "limit N" with a number. Implements `Expr`:

  • If 0 -> append nothing.
  • Otherwise -> append literal "limit <N>" such as "limit 1".

Because this is uint64, you can safely and correctly decode arbitrary user input into this value, for example into a struct field of this type.

Example
package main

import (
	"fmt"

	s "github.com/mitranim/sqlb"
)

func main() {
	fmt.Println(s.Reify(
		s.Exprs{s.Select{`some_table`, nil}, s.LimitUint(10)},
	))
}
Output:

select * from "some_table" limit 10 []

func (LimitUint) AppendExpr added in v0.4.2

func (self LimitUint) AppendExpr(text []byte, args []any) ([]byte, []any)

Implement the `Expr` interface, making this a sub-expression.

func (LimitUint) AppendTo added in v0.7.0

func (self LimitUint) AppendTo(text []byte) []byte

Implement the `AppenderTo` interface, sometimes allowing more efficient text encoding.

func (LimitUint) String added in v0.4.2

func (self LimitUint) String() string

Implement the `fmt.Stringer` interface for debug purposes.

type List added in v0.2.0

type List []any

Variant of `[]any` conforming to the `ArgDict` interface. Supports only ordinal parameters, not named parameters. Used for `StrQ`. See the `ListQ` shortcut.

func (List) GotNamed added in v0.2.0

func (self List) GotNamed(string) (any, bool)

Implement part of the `ArgDict` interface. Always returns `nil, false`.

func (List) GotOrdinal added in v0.2.0

func (self List) GotOrdinal(key int) (any, bool)

Implement part of the `ArgDict` interface.

func (List) IsEmpty added in v0.2.0

func (self List) IsEmpty() bool

Implement part of the `ArgDict` interface.

func (List) Len added in v0.2.0

func (self List) Len() int

Implement part of the `ArgDict` interface.

func (List) RangeOrdinal added in v0.2.0

func (self List) RangeOrdinal(fun func(int))

Implement `OrdinalRanger` to automatically validate used/unused arguments.

type NamedParam added in v0.2.0

type NamedParam string

Represents a named parameter such as ":blah". Mostly for internal use.

func (NamedParam) AppendExpr added in v0.2.0

func (self NamedParam) AppendExpr(text []byte, args []any) ([]byte, []any)

Implement the `Expr` interface, making this a sub-expression.

func (NamedParam) AppendTo added in v0.7.0

func (self NamedParam) AppendTo(text []byte) []byte

Implement the `AppenderTo` interface, sometimes allowing more efficient text encoding.

func (NamedParam) Key added in v0.2.0

func (self NamedParam) Key() string

Converts to the corresponding dictionary key, which is a plain string. This is a free cast, used to increase code clarity.

func (NamedParam) String added in v0.2.0

func (self NamedParam) String() string

Implement the `fmt.Stringer` interface for debug purposes.

type NamedRanger added in v0.2.0

type NamedRanger interface {
	/**
	Must iterate over known argument names, calling the function for each name.
	The func is provided by this package, and will panic for each unused
	argument.
	*/
	RangeNamed(func(string))
}

Optional extension for `ArgDict`. If implemented, this is used to validate used/unused named arguments after building a parametrized SQL expression such as `StrQ`/`Prep`.

type Neq added in v0.2.0

type Neq [2]any

Short for "not equal". Represents SQL non-equality such as `A <> B` or `A is not null`. Counterpart to `Eq`.

Example
package main

import (
	"fmt"

	s "github.com/mitranim/sqlb"
)

func main() {
	fmt.Println(s.Reify(s.Neq{10, 20}))

	fmt.Println(s.Reify(s.Neq{
		s.Ident(`some_col`),
		nil,
	}))

	fmt.Println(s.Reify(s.Neq{
		s.Ident(`some_col`),
		s.Ident(`another_col`),
	}))

	fmt.Println(s.Reify(s.Neq{
		s.Ident(`some_col`),
		s.Path{`some_table`, `another_col`},
	}))

}
Output:

$1 <> $2 [10 20]
("some_col") is not null []
("some_col") <> ("another_col") []
("some_col") <> (("some_table")."another_col") []

func (Neq) AppendExpr added in v0.2.0

func (self Neq) AppendExpr(text []byte, args []any) ([]byte, []any)

Implement the `Expr` interface, making this a sub-expression.

func (Neq) AppendLhs added in v0.2.0

func (self Neq) AppendLhs(text []byte, args []any) ([]byte, []any)

See the comment on `Eq.AppendLhs`.

func (Neq) AppendRhs added in v0.2.0

func (self Neq) AppendRhs(text []byte, args []any) ([]byte, []any)

func (Neq) AppendTo added in v0.7.0

func (self Neq) AppendTo(text []byte) []byte

Implement the `AppenderTo` interface, sometimes allowing more efficient text encoding.

func (Neq) String added in v0.2.0

func (self Neq) String() string

Implement the `fmt.Stringer` interface for debug purposes.

type NeqAny added in v0.2.0

type NeqAny [2]any

Represents an SQL expression `A <> any(B)`. Counterpart to `EqAny`.

Example
package main

import (
	"fmt"

	s "github.com/mitranim/sqlb"
)

func main() {
	fmt.Println(s.Reify(s.NeqAny{
		10,
		[]int{20, 30},
	}))

	fmt.Println(s.Reify(s.NeqAny{
		s.Ident(`some_col`),
		[]int{20, 30},
	}))

	fmt.Println(s.Reify(s.NeqAny{
		s.Ident(`some_col`),
		s.Table{`some_table`},
	}))

}
Output:

$1 <> any ($2) [10 [20 30]]
("some_col") <> any ($1) [[20 30]]
("some_col") <> any (table "some_table") []

func (NeqAny) AppendExpr added in v0.2.0

func (self NeqAny) AppendExpr(text []byte, args []any) ([]byte, []any)

Implement the `Expr` interface, making this a sub-expression.

func (NeqAny) AppendTo added in v0.7.0

func (self NeqAny) AppendTo(text []byte) []byte

Implement the `AppenderTo` interface, sometimes allowing more efficient text encoding.

func (NeqAny) String added in v0.2.0

func (self NeqAny) String() string

Implement the `fmt.Stringer` interface for debug purposes.

type Not added in v0.2.0

type Not [1]any

Represents SQL logical negation such as `not A`. The inner value can be an instance of `Expr` or an arbitrary argument.

Example
package main

import (
	"fmt"

	s "github.com/mitranim/sqlb"
)

func main() {
	fmt.Println(s.Reify(s.Not{}))
	fmt.Println(s.Reify(s.Not{true}))
	fmt.Println(s.Reify(s.Not{s.Ident(`some_col`)}))
}
Output:

not $1 [<nil>]
not $1 [true]
not ("some_col") []

func (Not) AppendExpr added in v0.2.0

func (self Not) AppendExpr(text []byte, args []any) ([]byte, []any)

Implement the `Expr` interface, making this a sub-expression.

func (Not) AppendTo added in v0.7.0

func (self Not) AppendTo(text []byte) []byte

Implement the `AppenderTo` interface, sometimes allowing more efficient text encoding.

func (Not) String added in v0.2.0

func (self Not) String() string

Implement the `fmt.Stringer` interface for debug purposes.

type Nullable added in v0.5.1

type Nullable interface{ IsNull() bool }

Optional interface that allows `sqlb` to determine if a given value is null, allowing some expressions to generate `is null` / `is not null` clauses. Not actually required; nils of Go nilable types are automatically considered null, and `sqlb` falls back on encoding the value via `driver.Valuer`. This interface is supported for additional flexibility and efficiency.

type Nulls added in v0.2.0

type Nulls byte

Enum for nulls handling in ordering: none, "nulls first", "nulls last".

const (
	NullsNone  Nulls = 0
	NullsFirst Nulls = 1
	NullsLast  Nulls = 2
)

func (Nulls) AppendTo added in v0.7.0

func (self Nulls) AppendTo(text []byte) []byte

Implement the `AppenderTo` interface, sometimes allowing more efficient text encoding.

func (Nulls) GoString added in v0.2.0

func (self Nulls) GoString() string

Implement `fmt.GoStringer` for debug purposes. Returns valid Go code representing this value.

func (Nulls) String added in v0.2.0

func (self Nulls) String() string

Implement `fmt.Stringer` for debug purposes.

type Offset added in v0.4.2

type Offset [1]any

Represents SQL expression "offset N" with an arbitrary sub-expression. Implements `Expr`:

  • If nil -> append nothing.
  • If expr -> append "offset (<sub-expression>)".
  • If val -> append "offset $N" with the corresponding argument.

func (Offset) AppendExpr added in v0.4.2

func (self Offset) AppendExpr(text []byte, args []any) ([]byte, []any)

Implement the `Expr` interface, making this a sub-expression.

func (Offset) AppendTo added in v0.7.0

func (self Offset) AppendTo(text []byte) []byte

Implement the `AppenderTo` interface, sometimes allowing more efficient text encoding.

func (Offset) String added in v0.4.2

func (self Offset) String() string

Implement the `fmt.Stringer` interface for debug purposes.

type OffsetUint added in v0.4.2

type OffsetUint uint64

Represents SQL expression "offset N" with a number. Implements `Expr`:

  • If 0 -> append nothing.
  • Otherwise -> append literal "offset <N>" such as "offset 1".

Because this is uint64, you can safely and correctly decode arbitrary user input into this value, for example into a struct field of this type.

Example
package main

import (
	"fmt"

	s "github.com/mitranim/sqlb"
)

func main() {
	fmt.Println(s.Reify(
		s.Exprs{s.Select{`some_table`, nil}, s.OffsetUint(10)},
	))
}
Output:

select * from "some_table" offset 10 []

func (OffsetUint) AppendExpr added in v0.4.2

func (self OffsetUint) AppendExpr(text []byte, args []any) ([]byte, []any)

Implement the `Expr` interface, making this a sub-expression.

func (OffsetUint) AppendTo added in v0.7.0

func (self OffsetUint) AppendTo(text []byte) []byte

Implement the `AppenderTo` interface, sometimes allowing more efficient text encoding.

func (OffsetUint) String added in v0.4.2

func (self OffsetUint) String() string

Implement the `fmt.Stringer` interface for debug purposes.

type Op added in v0.2.0

type Op byte

Syntax type of SQL operator expressions used in JEL. Allows us to convert JEL Lisp-style "calls" into SQL-style operations that use prefix, infix, etc.

const (
	OpPrefix Op = iota + 1
	OpPostfix
	OpInfix
	OpFunc
	OpAny
	OpBetween
)

type Or added in v0.2.0

type Or [1]any

Represents a sequence of arbitrary sub-expressions or arguments joined by the SQL `or` operator. Rules for the inner value:

  • nil or empty -> fallback to `false`
  • single `Expr` -> render it as-is
  • non-empty slice -> render its individual elements joined by `or`
  • non-empty struct -> render column equality conditions joined by `or`
Example (Slice)
package main

import (
	"fmt"

	s "github.com/mitranim/sqlb"
)

func main() {
	type list = []any

	fmt.Println(s.Reify(s.Or{nil}))
	fmt.Println(s.Reify(s.Or{list{}}))
	fmt.Println(s.Reify(s.Or{list{true, false, s.Ident(`some_col`)}}))

}
Output:

false []
false []
$1 or $2 or ("some_col") [true false]
Example (Struct)
package main

import (
	"fmt"

	s "github.com/mitranim/sqlb"
)

func main() {
	fmt.Println(s.Reify(s.Or{struct{}{}}))

	fmt.Println(s.Reify(s.Or{struct {
		Col0 bool `db:"col0"`
		Col1 any  `db:"col1"`
		Col2 any  `db:"col2"`
	}{
		true,
		nil,
		s.Call{`some_func`, []int{10}},
	}}))

}
Output:

false []
"col0" = $1 or "col1" is null or "col2" = (some_func ($2)) [true 10]

func (Or) AppendExpr added in v0.2.0

func (self Or) AppendExpr(text []byte, args []any) ([]byte, []any)

Implement the `Expr` interface, making this a sub-expression.

func (Or) AppendTo added in v0.7.0

func (self Or) AppendTo(text []byte) []byte

Implement the `AppenderTo` interface, sometimes allowing more efficient text encoding.

func (Or) String added in v0.2.0

func (self Or) String() string

Implement the `fmt.Stringer` interface for debug purposes.

type Ord added in v0.1.10

type Ord struct {
	Path  Path
	Dir   Dir
	Nulls Nulls
}

Structured representation of an arbitrary SQL ordering expression. This is not the entire "order by" clause (see `Ords`), but rather just one element in that clause. Also see `Ords`, `OrdsParser`, and the various provided examples.

func (Ord) AppendExpr added in v0.2.0

func (self Ord) AppendExpr(text []byte, args []any) ([]byte, []any)

Implement the `Expr` interface, making this a sub-expression.

func (Ord) AppendTo added in v0.7.0

func (self Ord) AppendTo(text []byte) []byte

Implement the `AppenderTo` interface, sometimes allowing more efficient text encoding.

func (Ord) IsEmpty added in v0.2.0

func (self Ord) IsEmpty() bool

True if the path is empty.

func (Ord) String added in v0.1.10

func (self Ord) String() string

Implement the `fmt.Stringer` interface for debug purposes.

type OrdAsc added in v0.1.10

type OrdAsc []string

Same as `Ord{Path: path, Dir: DirAsc}` but more syntactically convenient and uses less memory.

func (OrdAsc) AppendExpr added in v0.2.0

func (self OrdAsc) AppendExpr(text []byte, args []any) ([]byte, []any)

Implement the `Expr` interface, making this a sub-expression.

func (OrdAsc) AppendTo added in v0.7.0

func (self OrdAsc) AppendTo(text []byte) []byte

Implement the `AppenderTo` interface, sometimes allowing more efficient text encoding.

func (OrdAsc) String added in v0.2.0

func (self OrdAsc) String() string

Implement the `fmt.Stringer` interface for debug purposes.

type OrdAscNullsFirst added in v0.2.0

type OrdAscNullsFirst []string

Same as `Ord{Path: path, Dir: DirAsc, Nulls: NullsFirst}` but more syntactically convenient and uses less memory.

func (OrdAscNullsFirst) AppendExpr added in v0.2.0

func (self OrdAscNullsFirst) AppendExpr(text []byte, args []any) ([]byte, []any)

Implement the `Expr` interface, making this a sub-expression.

func (OrdAscNullsFirst) AppendTo added in v0.7.0

func (self OrdAscNullsFirst) AppendTo(text []byte) []byte

Implement the `AppenderTo` interface, sometimes allowing more efficient text encoding.

func (OrdAscNullsFirst) String added in v0.2.0

func (self OrdAscNullsFirst) String() string

Implement the `fmt.Stringer` interface for debug purposes.

type OrdAscNullsLast added in v0.2.0

type OrdAscNullsLast []string

Same as `Ord{Path: path, Dir: DirAsc, Nulls: NullsLast}` but more syntactically convenient and uses less memory.

func (OrdAscNullsLast) AppendExpr added in v0.2.0

func (self OrdAscNullsLast) AppendExpr(text []byte, args []any) ([]byte, []any)

Implement the `Expr` interface, making this a sub-expression.

func (OrdAscNullsLast) AppendTo added in v0.7.0

func (self OrdAscNullsLast) AppendTo(text []byte) []byte

Implement the `AppenderTo` interface, sometimes allowing more efficient text encoding.

func (OrdAscNullsLast) String added in v0.2.0

func (self OrdAscNullsLast) String() string

Implement the `fmt.Stringer` interface for debug purposes.

type OrdDesc added in v0.1.10

type OrdDesc []string

Same as `Ord{Path: path, Dir: DirDesc}` but more syntactically convenient and uses less memory.

func (OrdDesc) AppendExpr added in v0.2.0

func (self OrdDesc) AppendExpr(text []byte, args []any) ([]byte, []any)

Implement the `Expr` interface, making this a sub-expression.

func (OrdDesc) AppendTo added in v0.7.0

func (self OrdDesc) AppendTo(text []byte) []byte

Implement the `AppenderTo` interface, sometimes allowing more efficient text encoding.

func (OrdDesc) String added in v0.2.0

func (self OrdDesc) String() string

Implement the `fmt.Stringer` interface for debug purposes.

type OrdDescNullsFirst added in v0.2.0

type OrdDescNullsFirst []string

Same as `Ord{Path: path, Dir: DirDesc, Nulls: NullsFirst}` but more syntactically convenient and uses less memory.

func (OrdDescNullsFirst) AppendExpr added in v0.2.0

func (self OrdDescNullsFirst) AppendExpr(text []byte, args []any) ([]byte, []any)

Implement the `Expr` interface, making this a sub-expression.

func (OrdDescNullsFirst) AppendTo added in v0.7.0

func (self OrdDescNullsFirst) AppendTo(text []byte) []byte

Implement the `AppenderTo` interface, sometimes allowing more efficient text encoding.

func (OrdDescNullsFirst) String added in v0.2.0

func (self OrdDescNullsFirst) String() string

Implement the `fmt.Stringer` interface for debug purposes.

type OrdDescNullsLast added in v0.2.0

type OrdDescNullsLast []string

Same as `Ord{Path: path, Dir: DirDesc, Nulls: NullsLast}` but more syntactically convenient and uses less memory.

func (OrdDescNullsLast) AppendExpr added in v0.2.0

func (self OrdDescNullsLast) AppendExpr(text []byte, args []any) ([]byte, []any)

Implement the `Expr` interface, making this a sub-expression.

func (OrdDescNullsLast) AppendTo added in v0.7.0

func (self OrdDescNullsLast) AppendTo(text []byte) []byte

Implement the `AppenderTo` interface, sometimes allowing more efficient text encoding.

func (OrdDescNullsLast) String added in v0.2.0

func (self OrdDescNullsLast) String() string

Implement the `fmt.Stringer` interface for debug purposes.

type OrdNullsFirst added in v0.2.0

type OrdNullsFirst []string

Same as `Ord{Path: path, Nulls: NullsFirst}` but more syntactically convenient and uses less memory.

func (OrdNullsFirst) AppendExpr added in v0.2.0

func (self OrdNullsFirst) AppendExpr(text []byte, args []any) ([]byte, []any)

Implement the `Expr` interface, making this a sub-expression.

func (OrdNullsFirst) AppendTo added in v0.7.0

func (self OrdNullsFirst) AppendTo(text []byte) []byte

Implement the `AppenderTo` interface, sometimes allowing more efficient text encoding.

func (OrdNullsFirst) String added in v0.2.0

func (self OrdNullsFirst) String() string

Implement the `fmt.Stringer` interface for debug purposes.

type OrdNullsLast added in v0.2.0

type OrdNullsLast []string

Same as `Ord{Path: path, Nulls: NullsLast}` but more syntactically convenient and uses less memory.

func (OrdNullsLast) AppendExpr added in v0.2.0

func (self OrdNullsLast) AppendExpr(text []byte, args []any) ([]byte, []any)

Implement the `Expr` interface, making this a sub-expression.

func (OrdNullsLast) AppendTo added in v0.7.0

func (self OrdNullsLast) AppendTo(text []byte) []byte

Implement the `AppenderTo` interface, sometimes allowing more efficient text encoding.

func (OrdNullsLast) String added in v0.2.0

func (self OrdNullsLast) String() string

Implement the `fmt.Stringer` interface for debug purposes.

type OrderBy added in v0.2.0

type OrderBy [1]Expr

If the provided expression is not nil, prepends the keywords "order by" to it. If the provided expression is nil, this is a nop.

func (OrderBy) AppendExpr added in v0.2.0

func (self OrderBy) AppendExpr(text []byte, args []any) ([]byte, []any)

Implement the `Expr` interface, making this a sub-expression.

func (OrderBy) AppendTo added in v0.7.0

func (self OrderBy) AppendTo(text []byte) []byte

Implement the `AppenderTo` interface, sometimes allowing more efficient text encoding.

func (OrderBy) String added in v0.2.0

func (self OrderBy) String() string

Implement the `fmt.Stringer` interface for debug purposes.

type Ordering added in v0.2.0

type Ordering struct {
	Expr  Expr
	Dir   Dir
	Nulls Nulls
	Using Expr
}

Structured representation of an arbitrary SQL ordering expression. This is not the entire "order by" clause (see `Ords`), but rather just one element in that clause. This is the general-case representation, but because most ordering expressions use only column names and direction, a more specialized representation is preferred: `Ord`. This is provided just-in-case.

func (Ordering) AppendExpr added in v0.2.0

func (self Ordering) AppendExpr(text []byte, args []any) ([]byte, []any)

Implement the `Expr` interface, making this a sub-expression.

func (Ordering) AppendTo added in v0.7.0

func (self Ordering) AppendTo(text []byte) []byte

Implement the `AppenderTo` interface, sometimes allowing more efficient text encoding.

func (Ordering) String added in v0.2.0

func (self Ordering) String() string

Implement the `fmt.Stringer` interface for debug purposes.

type OrdinalParam added in v0.2.0

type OrdinalParam int

Represents an ordinal parameter such as "$1". Mostly for internal use.

func (OrdinalParam) AppendExpr added in v0.2.0

func (self OrdinalParam) AppendExpr(text []byte, args []any) ([]byte, []any)

Implement the `Expr` interface, making this a sub-expression.

func (OrdinalParam) AppendTo added in v0.7.0

func (self OrdinalParam) AppendTo(text []byte) []byte

Implement the `AppenderTo` interface, sometimes allowing more efficient text encoding.

func (OrdinalParam) FromIndex added in v0.2.0

func (self OrdinalParam) FromIndex() OrdinalParam

Inverse of `OrdinalParam.Index`: increments by 1, converting index to param.

func (OrdinalParam) Index added in v0.2.0

func (self OrdinalParam) Index() int

Returns the corresponding Go index (starts at zero).

func (OrdinalParam) String added in v0.2.0

func (self OrdinalParam) String() string

Implement the `fmt.Stringer` interface for debug purposes.

type OrdinalRanger added in v0.2.0

type OrdinalRanger interface {
	/**
	Must iterate over argument indexes from 0 to N, calling the function for each
	index. The func is provided by this package, and will panic for each unused
	argument.
	*/
	RangeOrdinal(func(int))
}

Optional extension for `ArgDict`. If implemented, this is used to validate used/unused ordinal arguments after building a parametrized SQL expression such as `StrQ`/`Prep`.

type Ords added in v0.1.10

type Ords []Expr

Short for "orderings". Sequence of arbitrary expressions used for an SQL "order by" clause. Nil elements are treated as non-existent. If there are no non-nil elements, the resulting expression is empty. Otherwise, the resulting expression is "order by" followed by comma-separated sub-expressions. You can construct `Ords` manually, or parse client inputs via `OrdsParser`. See the examples.

Example (Empty)
package main

import (
	"fmt"

	s "github.com/mitranim/sqlb"
)

func main() {
	fmt.Println(s.Ords{})
}
Output:

Example (Manual)
package main

import (
	"fmt"

	s "github.com/mitranim/sqlb"
)

func main() {
	fmt.Println(s.Ords{
		s.OrdDesc{`col0`},
		s.Str(`random() asc`),
	})
}
Output:

order by "col0" desc, random() asc

func (*Ords) Add added in v0.5.0

func (self *Ords) Add(vals ...Expr)

Convenience method for appending.

func (Ords) AppendExpr added in v0.2.0

func (self Ords) AppendExpr(text []byte, args []any) ([]byte, []any)

Implement the `Expr` interface, making this a sub-expression.

func (Ords) AppendTo added in v0.7.0

func (self Ords) AppendTo(text []byte) []byte

Implement the `AppenderTo` interface, sometimes allowing more efficient text encoding.

func (*Ords) Grow added in v0.2.0

func (self *Ords) Grow(size int)

Resizes to ensure that space capacity is `<= size`.

func (Ords) IsEmpty added in v0.1.10

func (self Ords) IsEmpty() bool

Returns true if there are no non-nil items.

func (Ords) Len added in v0.1.10

func (self Ords) Len() (count int)

Returns the amount of non-nil items.

func (Ords) MarshalJSON added in v0.5.0

func (self Ords) MarshalJSON() ([]byte, error)

Allows types that embed `Ords` to behave like a slice in JSON encoding, avoiding some edge issues. For example, this allows an empty `ParserOrds` to be encoded as JSON `null` rather than a struct, allowing types that include it as a field to be used for encoding JSON, not just decoding it. However, this doesn't make ords encoding/decoding actually reversible. Decoding "consults" a struct type to convert JSON field names to DB column names. Ideally, JSON marshaling would perform the same process in reverse, which is not currently implemented.

func (*Ords) Or added in v0.1.10

func (self *Ords) Or(vals ...Expr)

If empty, sets the given vals. Otherwise it's a nop.

func (*Ords) OrdsParser added in v0.5.0

func (self *Ords) OrdsParser(typ any) (out OrdsParser)

Returns an `OrdsParser` that can decode arbitrary JSON or a string slice into the given `*Ords` pointer. Initializes the parser to the provided type, using `typ` only as a type carrier.

Example
package main

import (
	"fmt"

	s "github.com/mitranim/sqlb"
)

func main() {
	type SomeStruct struct {
		Col0 string `json:"jsonField0" db:"dbCol0"`
		Col1 string `json:"jsonField1" db:"dbCol1"`
	}

	var ords s.Ords
	err := ords.OrdsParser((*SomeStruct)(nil)).ParseSlice([]string{
		`jsonField0 asc`,
		`jsonField1 desc nulls last`,
	})
	if err != nil {
		panic(err)
	}

	fmt.Printf("%#v\n\n", ords)
	fmt.Println(ords)
}
Output:

sqlb.Ords{sqlb.OrdAsc{"dbCol0"}, sqlb.OrdDescNullsLast{"dbCol1"}}

order by "dbCol0" asc, "dbCol1" desc nulls last

func (*Ords) OrdsPtr added in v0.5.0

func (self *Ords) OrdsPtr() *Ords

Sometimes handy for types that embed `Ords`.

func (Ords) RowNumberOver added in v0.2.0

func (self Ords) RowNumberOver() RowNumberOver

Returns an expression for the Postgres window function `row_number`:

Ords{}.RowNumber()
-> `0`

Ords{OrdAsc(`col`)}.RowNumber()
-> `row_number() over (order by "col" asc)`

As shown above, empty `Ords` generates `0`. The Postgres query planner should optimize away any ordering by this constant column.

func (Ords) String added in v0.2.0

func (self Ords) String() string

Implement the `fmt.Stringer` interface for debug purposes.

func (*Ords) Zero added in v0.5.0

func (self *Ords) Zero()

Empties the receiver. If the receiver was non-nil, its length is reduced to 0 while keeping any capacity, and it remains non-nil.

type OrdsParser added in v0.2.0

type OrdsParser struct {
	*Ords
	ParseOpt
}

Similar to `ParserOrds`, but intended to be transient and stackframe-local, rather than included into other types. Usually obtained by calling `(*Ords).OrdsParser`.

func (OrdsParser) ParseSlice added in v0.2.0

func (self OrdsParser) ParseSlice(src []string) (err error)

See `(*ParserOrds).ParseSlice` for docs.

func (OrdsParser) UnmarshalJSON added in v0.2.0

func (self OrdsParser) UnmarshalJSON(src []byte) (err error)

Implement `json.Unmarshaler`. See `(*ParserOrds).UnmarshalJSON` for docs.

type Ors added in v0.2.0

type Ors []any

Syntactic shortcut, same as `Or` with a slice of sub-expressions or arguments.

Example
package main

import (
	"fmt"

	s "github.com/mitranim/sqlb"
)

func main() {
	fmt.Println(s.Reify(s.Ors{}))
	fmt.Println(s.Reify(s.Ors{true, false, s.Ident(`some_col`)}))
}
Output:

false []
$1 or $2 or ("some_col") [true false]

func (Ors) AppendExpr added in v0.2.0

func (self Ors) AppendExpr(text []byte, args []any) ([]byte, []any)

Implement the `Expr` interface, making this a sub-expression.

func (Ors) AppendTo added in v0.7.0

func (self Ors) AppendTo(text []byte) []byte

Implement the `AppenderTo` interface, sometimes allowing more efficient text encoding.

func (Ors) String added in v0.2.0

func (self Ors) String() string

Implement the `fmt.Stringer` interface for debug purposes.

type ParamExpr added in v0.2.0

type ParamExpr interface {
	AppendParamExpr([]byte, []any, ArgDict) ([]byte, []any)
}

Short for "parametrized expression". Similar to `Expr`, but requires an external input in order to be a valid expression. Implemented by preparsed query types, namely by `Prep`.

type ParseOpt added in v0.5.0

type ParseOpt struct {
	/**
	Must be a struct type. Ords parsing uses this to detect which fields are
	allowed, and to convert JSON field names to DB column names.
	*/
	Type r.Type

	/**
	Optional filter. When non-nil, this is invoked for each struct field during
	ords parsing. If this returns false, the field is "unknown" and may generate
	a parse error depending on `.Lax`.
	*/
	Filter Filter

	/**
	When true, unknown JSON fields are skipped/ignored durung parsing. When false,
	unknown JSON fields cause ords parsing to fail with a descriptive error.
	*/
	Lax bool
}

Options related to parsing text into `Ords`. Used by `ParserOrds` and `OrdsParser`.

func (*ParseOpt) OrType added in v0.5.0

func (self *ParseOpt) OrType(typ any)

If `.Type` is empty, sets the type of the provided value. Otherwise this is a nop. The input is used only as a type carrier; its actual value is ignored. The type is consulted when decoding orderings from an input such as JSON.

type ParserOrds added in v0.5.0

type ParserOrds struct {
	Ords
	ParseOpt
}

Contains `Ords` and parsing options, and implements decoder interfaces such as `json.Unmarshaler`. Intended to be included into other structs. Unmarshals text into inner `Ords` in accordance with the parsing options.

func (*ParserOrds) ParseSlice added in v0.5.0

func (self *ParserOrds) ParseSlice(src []string) error

Parses a string slice which must consist of individual ordering strings such as "one.two.three desc". Ignores empty strings. Used internally for parsing JSON. String slices may also come from URL queries, form-encoded data, and so on. Supported input format:

<path> <asc|desc>? <nulls first | nulls last>?

Each path can be a single identifier or dot-separated:

one
one.two
one.two.three

The path MUST correspond to JSON-tagged fields in the reference struct type, which MUST have corresponding DB column names. The parsed ordering uses DB column names, rather than the original JSON names.

Example
package main

import (
	"fmt"

	s "github.com/mitranim/sqlb"
)

func main() {
	type SomeStruct struct {
		Col0 string `json:"jsonField0" db:"dbCol0"`
		Col1 string `json:"jsonField1" db:"dbCol1"`
	}

	var par s.ParserOrds
	par.OrType((*SomeStruct)(nil))

	err := par.ParseSlice([]string{`jsonField0 asc`, `jsonField1 desc nulls last`})
	if err != nil {
		panic(err)
	}

	fmt.Printf("%#v\n\n", par.Ords)
	fmt.Println(par.Ords)
}
Output:

sqlb.Ords{sqlb.OrdAsc{"dbCol0"}, sqlb.OrdDescNullsLast{"dbCol1"}}

order by "dbCol0" asc, "dbCol1" desc nulls last

func (*ParserOrds) UnmarshalJSON added in v0.5.0

func (self *ParserOrds) UnmarshalJSON(src []byte) error

Implement `json.Unmarshaler`. Consults `.Type` to determine known field paths, and converts them to DB column paths, rejecting unknown identifiers. The JSON input must represent an array of strings. See the method `.ParseSlice` for more docs.

Example
package main

import (
	"encoding/json"
	"fmt"

	s "github.com/mitranim/sqlb"
)

func main() {
	type SomeStruct struct {
		Col0 string `json:"jsonField0" db:"dbCol0"`
		Col1 string `json:"jsonField1" db:"dbCol1"`
	}

	var par s.ParserOrds
	par.OrType((*SomeStruct)(nil))

	err := json.Unmarshal(
		[]byte(`["jsonField0 asc", "jsonField1 desc nulls last"]`),
		&par,
	)
	if err != nil {
		panic(err)
	}

	fmt.Printf("%#v\n\n", par.Ords)
	fmt.Println(par.Ords)
}
Output:

sqlb.Ords{sqlb.OrdAsc{"dbCol0"}, sqlb.OrdDescNullsLast{"dbCol1"}}

order by "dbCol0" asc, "dbCol1" desc nulls last

type Partial added in v0.2.1

type Partial struct {
	Val any
	Fil Haser
}

Implements `Sparse` by filtering fields on their JSON names, using only explicit "json" tags. Fields without explicit "json" names are automatically considered missing. Fields with "json" tags must be present in the provided string set represented by `.Fil`.

Designed for compatibility with HTTP request decoders provided by "github.com/mitranim/rd", which either implement `Haser` or can easily generate one. Example PATCH endpoint using "rd":

import "github.com/mitranim/rd"
import "github.com/mitranim/try"
import s "github.com/mitranim/sqlb"

dec := rd.TryDownload(req)

var input SomeStructType
try.To(dec.Decode(&input))

expr := s.Exprs{
	s.Update{s.Ident(`some_table`)},
	s.Set{s.StructAssign{s.Partial{input, dec.Haser()}}},
}

func (Partial) AllowField added in v0.5.0

func (self Partial) AllowField(field r.StructField) bool

Implement `Sparse`, using the underlying filter.

func (Partial) Get added in v0.2.1

func (self Partial) Get() any

Implement `Sparse`, returning the underlying value.

type Path added in v0.2.0

type Path []string

Represents a nested SQL identifier where the first outer element is parenthesized, and every element is quoted. Useful for nested paths that begin with a table or view name. For schema-qualified paths, use `Identifier` instead.

Example
package main

import (
	"fmt"

	s "github.com/mitranim/sqlb"
)

func main() {
	fmt.Println(s.Path{`one`})
	fmt.Println(s.Path{`one`, `two`})
	fmt.Println(s.Path{`one`, `two`, `three`})
}
Output:

"one"
("one")."two"
("one")."two"."three"

func (Path) AppendExpr added in v0.2.0

func (self Path) AppendExpr(text []byte, args []any) ([]byte, []any)

Implement the `Expr` interface, making this a sub-expression.

func (Path) AppendTo added in v0.7.0

func (self Path) AppendTo(text []byte) []byte

Implement the `AppenderTo` interface, sometimes allowing more efficient text encoding.

func (Path) Norm added in v0.2.0

func (self Path) Norm() Expr

Normalizes the expression, returning nil or a single `Ident` if the length allows this. Otherwise returns self as-is.

func (Path) String added in v0.2.0

func (self Path) String() string

Implement the `fmt.Stringer` interface for debug purposes.

type Prefix added in v0.2.0

type Prefix struct {
	Prefix string
	Expr   Expr
}

Combines an expression with a string prefix. If the expr is nil, this is a nop, and the prefix is ignored. Mostly an internal tool for building other expression types.

func (Prefix) AppendExpr added in v0.2.0

func (self Prefix) AppendExpr(text []byte, args []any) ([]byte, []any)

Implement the `Expr` interface, making this a sub-expression.

func (Prefix) AppendTo added in v0.7.0

func (self Prefix) AppendTo(text []byte) []byte

Implement the `AppenderTo` interface, sometimes allowing more efficient text encoding.

func (Prefix) String added in v0.2.0

func (self Prefix) String() string

Implement the `fmt.Stringer` interface for debug purposes.

type Prep added in v0.2.0

type Prep struct {
	Source    string
	Tokens    []Token
	HasParams bool
}

Short for "preparsed" or "prepared". Partially parsed representation of parametrized SQL expressions, suited for efficiently building SQL queries by providing arguments. Supports both ordinal and named parameters/arguments. To avoid redundant work, this should be parsed and cached only once for each SQL query; this deduplication is done by `Preparse` which is also used internally by `StrQ`. User code doesn't need to construct this.

func Preparse added in v0.2.0

func Preparse(val string) Prep

Returns a parsed `Prep` for the given source string. Panics if parsing fails. Caches the result for each source string, reusing it for future calls. Used internally by `StrQ`. User code shouldn't have to call this, but it's exported just in case.

func (Prep) AppendParamExpr added in v0.2.0

func (self Prep) AppendParamExpr(text []byte, args []any, dict ArgDict) ([]byte, []any)

Implement the `ParamExpr` interface. Builds the expression by using the provided named args. Used internally by `StrQ`.

func (Prep) AppendTo added in v0.7.0

func (self Prep) AppendTo(text []byte) []byte

Implement the `AppenderTo` interface, sometimes allowing more efficient text encoding.

func (*Prep) Parse added in v0.2.0

func (self *Prep) Parse()

Parses `self.Source`, modifying the receiver. Panics if parsing fails.

func (Prep) String added in v0.2.0

func (self Prep) String() string

Implement the `fmt.Stringer` interface for debug purposes.

type PseudoPath added in v0.2.0

type PseudoPath []string

Represents an arbitrarily-nested SQL path that gets encoded as a SINGLE quoted identifier, where elements are dot-separated. This is a common convention for nested structs, supported by SQL-scanning libraries such as https://github.com/mitranim/gos.

Example
package main

import (
	"fmt"

	s "github.com/mitranim/sqlb"
)

func main() {
	fmt.Println(s.PseudoPath{`one`})
	fmt.Println(s.PseudoPath{`one`, `two`})
	fmt.Println(s.PseudoPath{`one`, `two`, `three`})
}
Output:

"one"
"one.two"
"one.two.three"

func (PseudoPath) AppendExpr added in v0.2.0

func (self PseudoPath) AppendExpr(text []byte, args []any) ([]byte, []any)

Implement the `Expr` interface, making this a sub-expression.

func (PseudoPath) AppendTo added in v0.7.0

func (self PseudoPath) AppendTo(text []byte) []byte

Implement the `AppenderTo` interface, sometimes allowing more efficient text encoding.

func (PseudoPath) Norm added in v0.2.0

func (self PseudoPath) Norm() Expr

Normalizes the expression, returning nil or a single `Ident` if the length allows this. Otherwise returns self as-is.

func (PseudoPath) String added in v0.2.0

func (self PseudoPath) String() string

Implement the `fmt.Stringer` interface for debug purposes.

type ReturningAll added in v0.7.1

type ReturningAll struct{}

Represents the Postgres `returning *` clause.

func (ReturningAll) AppendExpr added in v0.7.1

func (self ReturningAll) AppendExpr(text []byte, args []any) ([]byte, []any)

Implement the `Expr` interface, making this a sub-expression.

func (ReturningAll) AppendTo added in v0.7.1

func (self ReturningAll) AppendTo(text []byte) []byte

Implement the `AppenderTo` interface, sometimes allowing more efficient text encoding.

func (ReturningAll) String added in v0.7.1

func (ReturningAll) String() string

Implement the `fmt.Stringer` interface for debug purposes.

type RowNumberOver added in v0.2.0

type RowNumberOver [1]Expr

Represents the Postgres window function `row_number`:

RowNumberOver{}
-> `0`

RowNumberOver{Ords{OrdDesc{Ident(`some_col`)}}}
-> `row_number() over (order by "col" desc)`

When the inner expression is nil and the output is `0`, the Postgres query planner should be able to optimize it away.

Example (Empty)
package main

import (
	"fmt"

	s "github.com/mitranim/sqlb"
)

func main() {
	fmt.Println(s.RowNumberOver{})
}
Output:

0
Example (NonEmpty)
package main

import (
	"fmt"

	s "github.com/mitranim/sqlb"
)

func main() {
	fmt.Println(s.RowNumberOver{s.Ords{s.OrdDesc{`some_col`}}})
}
Output:

row_number() over (order by "some_col" desc)

func (RowNumberOver) AppendExpr added in v0.2.0

func (self RowNumberOver) AppendExpr(text []byte, args []any) ([]byte, []any)

Implement the `Expr` interface, making this a sub-expression.

func (RowNumberOver) AppendTo added in v0.7.0

func (self RowNumberOver) AppendTo(text []byte) []byte

Implement the `AppenderTo` interface, sometimes allowing more efficient text encoding.

func (RowNumberOver) String added in v0.2.0

func (self RowNumberOver) String() string

Implement the `fmt.Stringer` interface for debug purposes.

type Select added in v0.2.0

type Select struct {
	From  Ident
	Where any
}

Shortcut for simple "select * from A where B" expressions. See the examples.

Example (Filtered)
package main

import (
	"fmt"

	s "github.com/mitranim/sqlb"
)

func main() {
	type Filter struct {
		Col0 int64 `db:"col0"`
		Col1 int64 `db:"col1"`
	}

	fmt.Println(s.Reify(s.Select{`some_table`, Filter{10, 20}}))
}
Output:

select * from "some_table" where "col0" = $1 and "col1" = $2 [10 20]
Example (Unfiltered)
package main

import (
	"fmt"

	s "github.com/mitranim/sqlb"
)

func main() {
	fmt.Println(s.Reify(s.Select{`some_table`, nil}))
}
Output:

select * from "some_table" []

func (Select) AppendExpr added in v0.2.0

func (self Select) AppendExpr(text []byte, args []any) ([]byte, []any)

Implement the `Expr` interface, making this a sub-expression.

func (Select) AppendTo added in v0.7.0

func (self Select) AppendTo(text []byte) []byte

Implement the `AppenderTo` interface, sometimes allowing more efficient text encoding.

func (Select) String added in v0.2.0

func (self Select) String() string

Implement the `fmt.Stringer` interface for debug purposes.

type SelectCols added in v0.2.0

type SelectCols struct {
	From Expr
	Type any
}

Wraps an arbitrary sub-expression, using `Cols{.Type}` to select specific columns from it. If `.Type` doesn't specify a set of columns, for example because it's not a struct type, then this uses the sub-expression as-is without wrapping. Counterpart to `SelectColsDeep`.

Example (AsIs)
package main

import (
	"fmt"

	s "github.com/mitranim/sqlb"
)

func main() {
	fmt.Println(s.SelectCols{s.Table{`some_table`}, nil})
}
Output:

table "some_table"
Example (Cols)
package main

import (
	"fmt"

	s "github.com/mitranim/sqlb"
)

func main() {
	type SomeStruct struct {
		Col0 string `db:"col0"`
		Col1 string `db:"col1"`
		Col2 string `db:"-"`
	}

	fmt.Println(s.SelectCols{s.Table{`some_table`}, (*SomeStruct)(nil)})
}
Output:

with _ as (table "some_table") select "col0", "col1" from _

func (SelectCols) AppendExpr added in v0.2.0

func (self SelectCols) AppendExpr(text []byte, args []any) ([]byte, []any)

Implement the `Expr` interface, making this a sub-expression.

func (SelectCols) AppendTo added in v0.7.0

func (self SelectCols) AppendTo(text []byte) []byte

Implement the `AppenderTo` interface, sometimes allowing more efficient text encoding.

func (SelectCols) String added in v0.2.0

func (self SelectCols) String() string

Implement the `fmt.Stringer` interface for debug purposes.

type SelectColsDeep added in v0.2.0

type SelectColsDeep struct {
	From Expr
	Type any
}

Wraps an arbitrary sub-expression, using `ColsDeep{.Type}` to select specific columns from it. If `.Type` doesn't specify a set of columns, for example because it's not a struct type, then this uses the sub-expression as-is without wrapping. Counterpart to `SelectCols`.

Example (AsIs)
package main

import (
	"fmt"

	s "github.com/mitranim/sqlb"
)

func main() {
	fmt.Println(s.SelectColsDeep{s.Table{`some_table`}, nil})
}
Output:

table "some_table"
Example (Cols)
package main

import (
	"fmt"

	s "github.com/mitranim/sqlb"
)

func main() {
	type SomeStruct struct {
		Outer string `db:"outer"`
		Inner struct {
			Name string `db:"name"`
		} `db:"inner"`
	}

	fmt.Println(s.SelectColsDeep{s.Table{`some_table`}, (*SomeStruct)(nil)})
}
Output:

with _ as (table "some_table") select "outer", ("inner")."name" as "inner.name" from _

func (SelectColsDeep) AppendExpr added in v0.2.0

func (self SelectColsDeep) AppendExpr(text []byte, args []any) ([]byte, []any)

Implement the `Expr` interface, making this a sub-expression.

func (SelectColsDeep) AppendTo added in v0.7.0

func (self SelectColsDeep) AppendTo(text []byte) []byte

Implement the `AppenderTo` interface, sometimes allowing more efficient text encoding.

func (SelectColsDeep) String added in v0.2.0

func (self SelectColsDeep) String() string

Implement the `fmt.Stringer` interface for debug purposes.

type SelectCount added in v0.4.3

type SelectCount [1]Expr

Shortcut for selecting `count(*)` from an arbitrary sub-expression. Equivalent to `s.SelectString{expr, "count(*)"}`.

Example
package main

import (
	"fmt"

	s "github.com/mitranim/sqlb"
)

func main() {
	fmt.Println(
		s.SelectCount{s.Table{`some_table`}},
	)
}
Output:

with _ as (table "some_table") select count(*) from _

func (SelectCount) AppendExpr added in v0.4.3

func (self SelectCount) AppendExpr(text []byte, args []any) ([]byte, []any)

Implement the `Expr` interface, making this a sub-expression.

func (SelectCount) AppendTo added in v0.7.0

func (self SelectCount) AppendTo(text []byte) []byte

Implement the `AppenderTo` interface, sometimes allowing more efficient text encoding.

func (SelectCount) String added in v0.4.3

func (self SelectCount) String() string

Implement the `fmt.Stringer` interface for debug purposes.

type SelectString added in v0.2.0

type SelectString struct {
	From Expr
	What string
}

Represents an SQL expression "select .What from (.From) as _". Mostly an internal tool for building other expression types. Used internally by `Cols` and `ColsDeep`; see their docs and examples.

func (SelectString) AppendExpr added in v0.2.0

func (self SelectString) AppendExpr(text []byte, args []any) ([]byte, []any)

Implement the `Expr` interface, making this a sub-expression.

func (SelectString) AppendTo added in v0.7.0

func (self SelectString) AppendTo(text []byte) []byte

Implement the `AppenderTo` interface, sometimes allowing more efficient text encoding.

func (SelectString) String added in v0.2.0

func (self SelectString) String() string

Implement the `fmt.Stringer` interface for debug purposes.

type Seq added in v0.2.0

type Seq struct {
	Empty string
	Delim string
	Val   any
}

Represents a sequence of arbitrary sub-expressions or arguments, joined with a customizable delimiter, with a customizable fallback in case of empty list. This is mostly an internal tool for building other sequences, such as `And` and `Or`. The inner value may be nil or a single `Expr`, otherwise it must be a slice.

func (Seq) AppendExpr added in v0.2.0

func (self Seq) AppendExpr(text []byte, args []any) ([]byte, []any)

Implement the `Expr` interface, making this a sub-expression.

func (Seq) AppendTo added in v0.7.0

func (self Seq) AppendTo(text []byte) []byte

Implement the `AppenderTo` interface, sometimes allowing more efficient text encoding.

func (Seq) String added in v0.2.0

func (self Seq) String() string

Implement the `fmt.Stringer` interface for debug purposes.

type SliceCommaAppender added in v0.5.3

type SliceCommaAppender [1]any

Intermediary tool for implementing SQL array encoding. The inner value must be either nil, a slice/array, or a pointer to a slice/array, where each element must implement `AppenderTo`. When `.AppendTo` or `.String` is called, this combines the text representations of the elements, separating them with a comma, while skipping any empty representations. The output will never contain a dangling leading comma, double comma, or leading trailing comma, unless they were explicitly generated by the inner encoders. Compare `CommaAppender` which itself is a slice.

func (SliceCommaAppender) AppendTo added in v0.7.0

func (self SliceCommaAppender) AppendTo(buf []byte) []byte

Implement `AppenderTo`. Appends comma-separated text representations of the inner encoders to the output buffer, skipping any empty representations.

func (SliceCommaAppender) String added in v0.5.3

func (self SliceCommaAppender) String() string

Implement `fmt.Stringer` by calling `.AppendTo`.

type Sparse added in v0.2.1

type Sparse interface {
	Filter
	Get() any
}

Represents an arbitrary struct where not all fields are "present". Calling `.Get` returns the underlying struct value. Calling `.AllowField` answers the question "is this field present?".

Secretly supported by struct-scanning expressions such as `StructInsert`, `StructAssign`, `StructValues`, `Cond`, and more. These types attempt to upcast the inner value to `Sparse`, falling back on using the inner value as-is. This allows to correctly implement REST "PATCH" semantics by using only the fields that were present in a particular HTTP request, while keeping this functionality optional.

Concrete implementation: `Partial`.

type Str added in v0.2.0

type Str string

Shortcut for interpolating strings into queries. Because this implements `Expr`, when used as an argument in another expression, this will be directly interpolated into the resulting query string. See the examples.

Example (StringInterpolation)
package main

import (
	"fmt"

	s "github.com/mitranim/sqlb"
)

func main() {
	fmt.Println(s.Reify(
		s.StrQ{
			`select :col from some_table where :col <> :val`,
			s.Dict{
				`col`: s.Str(`some_col`),
				`val`: `some_val`,
			},
		},
	))
}
Output:

select some_col from some_table where some_col <> $1 [some_val]

func (Str) AppendExpr added in v0.2.0

func (self Str) AppendExpr(text []byte, args []any) ([]byte, []any)

Implement the `Expr` interface, making this a sub-expression.

func (Str) AppendTo added in v0.7.0

func (self Str) AppendTo(text []byte) []byte

Implement the `AppenderTo` interface, sometimes allowing more efficient text encoding.

func (Str) String added in v0.2.0

func (self Str) String() string

Implement the `fmt.Stringer` interface for debug purposes.

type StrQ added in v0.2.0

type StrQ struct {
	Text string
	Args ArgDict
}

Short for "string query". Represents an SQL query with parameters such as "$1" or ":param_name". Args may be a list of ordinal args (via `List`), a dictionary (via `Dict`), a struct (via `StructDict`), or an arbitrary user-defined implementation conforming to the interface. When generating the final expression, parameters are converted to Postgres-style ordinal parameters such as "$1".

Expressions/queries are composable. Named arguments that implement the `Expr` interface do not become ordinal parameters/arguments. Instead, they're treated as sub-expressions, and may include arbitrary text with their own arguments. Parameter collisions between outer and inner queries are completely avoided.

Uses `Preparse` to avoid redundant parsing. Each source string is parsed only once, and the resulting `Prep` is cached. As a result, `StrQ` has little measurable overhead.

Example (Empty)
package main

import (
	"fmt"

	s "github.com/mitranim/sqlb"
)

func main() {
	fmt.Println(s.Reify(s.StrQ{}))
}
Output:

[]
Example (Nested)
package main

import (
	"fmt"

	s "github.com/mitranim/sqlb"
)

func main() {
	inner := s.StrQ{
		`select * from some_table where col0 = :val`,
		s.Dict{`val`: 10},
	}

	outer := s.StrQ{
		`select * from (:inner) as _ where col1 = :val`,
		s.Dict{`inner`: inner, `val`: 20},
	}

	fmt.Println(s.Reify(outer))
}
Output:

select * from (select * from some_table where col0 = $1) as _ where col1 = $2 [10 20]
Example (Simple)
package main

import (
	"fmt"

	s "github.com/mitranim/sqlb"
)

func main() {
	fmt.Println(s.Reify(
		s.StrQ{
			`select * from some_table where col_one = :one and col_two = :two`,
			s.Dict{
				`one`: 10,
				`two`: 20,
			},
		},
	))
}
Output:

select * from some_table where col_one = $1 and col_two = $2 [10 20]
Example (StructInput)
package main

import (
	"fmt"

	s "github.com/mitranim/sqlb"
)

func main() {
	type Output struct {
		Col0 string `db:"col0"`
		Col1 string `db:"col1"`
	}

	type Filter struct {
		Col2 int64 `db:"col2"`
		Col3 int64 `db:"col3"`
	}

	type Input struct {
		Cols   s.Cols
		Filter s.And
	}

	fmt.Println(s.Reify(
		s.StructQ(`
			select :Cols from some_table where :Filter
		`, Input{
			s.Cols{(*Output)(nil)},
			s.And{Filter{10, 20}},
		}),
	))
}
Output:

select "col0", "col1" from some_table where "col2" = $1 and "col3" = $2 [10 20]
Example (Structs)
package main

import (
	"fmt"

	s "github.com/mitranim/sqlb"
)

func main() {
	type Output struct {
		Col0 string `db:"col0"`
		Col1 string `db:"col1"`
	}

	type Filter struct {
		Col2 int64 `db:"col2"`
		Col3 int64 `db:"col3"`
	}

	fmt.Println(s.Reify(
		s.StrQ{
			`select :cols from some_table where :filter`,
			s.Dict{
				`cols`:   s.Cols{(*Output)(nil)},
				`filter`: s.And{Filter{10, 20}},
			},
		},
	))
}
Output:

select "col0", "col1" from some_table where "col2" = $1 and "col3" = $2 [10 20]

func DictQ added in v0.2.0

func DictQ(text string, args map[string]any) StrQ

Shortcut for `StrQ{text, Dict(args)}`.

Example
package main

import (
	"fmt"

	s "github.com/mitranim/sqlb"
)

func main() {
	fmt.Println(s.Reify(
		s.DictQ(`
			select * from some_table where col_one = :one and col_two = :two
		`, map[string]any{
			`one`: 10,
			`two`: 20,
		}),
	))
}
Output:

select * from some_table where col_one = $1 and col_two = $2 [10 20]

func ListQ added in v0.2.0

func ListQ(text string, args ...any) StrQ

Shortcut for `StrQ{text, List(args)}`.

Example
package main

import (
	"fmt"

	s "github.com/mitranim/sqlb"
)

func main() {
	fmt.Println(s.Reify(
		s.ListQ(`
			select * from some_table where col_one = $1 and col_two = $2
		`, 10, 20),
	))
}
Output:

select * from some_table where col_one = $1 and col_two = $2 [10 20]

func StructQ added in v0.2.0

func StructQ(text string, args any) StrQ

Shortcut for `StrQ{text, StructDict{reflect.ValueOf(args)}}`.

func (StrQ) AppendExpr added in v0.2.0

func (self StrQ) AppendExpr(text []byte, args []any) ([]byte, []any)

Implement the `Expr` interface, making this a sub-expression.

func (StrQ) AppendTo added in v0.7.0

func (self StrQ) AppendTo(text []byte) []byte

Implement the `AppenderTo` interface, sometimes allowing more efficient text encoding.

func (StrQ) String added in v0.2.0

func (self StrQ) String() string

Implement the `fmt.Stringer` interface for debug purposes.

type StructAssign added in v0.2.0

type StructAssign [1]any

Represents an SQL assignment clause suitable for "update set" operations. The inner value must be a struct. The resulting expression consists of comma-separated assignments with column names and values derived from the provided struct. See the example.

Supports filtering. If the inner value implements `Sparse`, then not all fields are considered to be "present", which is useful for PATCH semantics. See the docs on `Sparse` and `Part`. If there are NO fields, panics with `ErrEmptyAssign`, which can be detected by user code via `errors.Is`.

Example
package main

import (
	"fmt"

	s "github.com/mitranim/sqlb"
)

func main() {
	fmt.Println(s.Reify(s.StructAssign{struct {
		Col0 bool `db:"col0"`
		Col1 any  `db:"col1"`
		Col2 any  `db:"col2"`
	}{
		true,
		nil,
		s.Call{`some_func`, []int{10}},
	}}))
}
Output:

"col0" = $1, "col1" = $2, "col2" = (some_func ($3)) [true <nil> 10]

func (StructAssign) AppendExpr added in v0.2.0

func (self StructAssign) AppendExpr(text []byte, args []any) ([]byte, []any)

Implement the `Expr` interface, making this a sub-expression.

func (StructAssign) AppendTo added in v0.7.0

func (self StructAssign) AppendTo(text []byte) []byte

Implement the `AppenderTo` interface, sometimes allowing more efficient text encoding.

func (StructAssign) String added in v0.2.0

func (self StructAssign) String() string

Implement the `fmt.Stringer` interface for debug purposes.

type StructDict added in v0.2.0

type StructDict [1]r.Value

Implements `ArgDict` by reading struct fields and methods by name. Supports only named parameters, not ordinal parameters. The inner value must be either invalid or a struct. Compared to `Dict`, a struct is way faster to construct, but reading fields by name is way slower. Used for `StrQ`. See the `StructQ` shortcut.

func (StructDict) GotNamed added in v0.2.0

func (self StructDict) GotNamed(key string) (any, bool)

Implement part of the `ArgDict` interface.

func (StructDict) GotOrdinal added in v0.2.0

func (self StructDict) GotOrdinal(int) (any, bool)

Implement part of the `ArgDict` interface. Always returns `nil, false`.

func (StructDict) IsEmpty added in v0.2.0

func (self StructDict) IsEmpty() bool

Implement part of the `ArgDict` interface.

func (StructDict) Len added in v0.2.0

func (self StructDict) Len() int

Implement part of the `ArgDict` interface. Always returns 0.

type StructInsert added in v0.2.0

type StructInsert [1]any

Represents a names-and-values clause suitable for insertion. The inner value must be nil or a struct. Nil or empty struct generates a "default values" clause. Otherwise the resulting expression has SQL column names and values generated by scanning the input struct. See the examples.

Supports filtering. If the inner value implements `Sparse`, then not all fields are considered to be "present", which is useful for PATCH semantics. See the docs on `Sparse` and `Part`.

Example (Empty)
package main

import (
	"fmt"

	s "github.com/mitranim/sqlb"
)

func main() {
	fmt.Println(s.StructInsert{})
}
Output:

default values
Example (NonEmpty)
package main

import (
	"fmt"

	s "github.com/mitranim/sqlb"
)

func main() {
	fmt.Println(s.Reify(s.StructInsert{struct {
		Col0 bool `db:"col0"`
		Col1 any  `db:"col1"`
		Col2 any  `db:"col2"`
	}{
		true,
		nil,
		s.Call{`some_func`, []int{10}},
	}}))
}
Output:

("col0", "col1", "col2") values ($1, $2, (some_func ($3))) [true <nil> 10]

func (StructInsert) AppendExpr added in v0.2.0

func (self StructInsert) AppendExpr(text []byte, args []any) ([]byte, []any)

Implement the `Expr` interface, making this a sub-expression.

func (StructInsert) AppendTo added in v0.7.0

func (self StructInsert) AppendTo(text []byte) []byte

Implement the `AppenderTo` interface, sometimes allowing more efficient text encoding.

func (StructInsert) String added in v0.2.0

func (self StructInsert) String() string

Implement the `fmt.Stringer` interface for debug purposes.

type StructValues added in v0.2.0

type StructValues [1]any

Represents comma-separated values from the "db"-tagged fields of an arbitrary struct. Field/column names are ignored. Values may be arbitrary sub-expressions or arguments. The value passed to `StructValues` may be nil, which is equivalent to an empty struct. It may also be an arbitrarily-nested struct pointer, which is automatically dereferenced.

Supports filtering. If the inner value implements `Sparse`, then not all fields are considered to be "present", which is useful for PATCH semantics. See the docs on `Sparse` and `Part`.

Example
package main

import (
	"fmt"

	s "github.com/mitranim/sqlb"
)

func main() {
	fmt.Println(s.Reify(s.StructValues{struct {
		Col0 bool `db:"col0"`
		Col1 any  `db:"col1"`
		Col2 any  `db:"col2"`
	}{
		true,
		nil,
		s.Call{`some_func`, []int{10}},
	}}))
}
Output:

$1, $2, (some_func ($3)) [true <nil> 10]

func (StructValues) AppendExpr added in v0.2.0

func (self StructValues) AppendExpr(text []byte, args []any) ([]byte, []any)

Implement the `Expr` interface, making this a sub-expression.

func (StructValues) AppendTo added in v0.7.0

func (self StructValues) AppendTo(text []byte) []byte

Implement the `AppenderTo` interface, sometimes allowing more efficient text encoding.

func (StructValues) String added in v0.2.0

func (self StructValues) String() string

Implement the `fmt.Stringer` interface for debug purposes.

type StructsInsert added in v0.6.1

type StructsInsert[A any] []A

Variant of `StructInsert` that supports multiple structs. Generates a names-and-values clause suitable for bulk insertion. The inner type must be a struct. An empty slice generates an empty expression. See the examples.

Example (Empty)
package main

import (
	"fmt"

	s "github.com/mitranim/sqlb"
)

func main() {
	fmt.Println(s.StructsInsertOf[any]())
}
Output:

Example (NonEmpty)
package main

import (
	"fmt"

	s "github.com/mitranim/sqlb"
)

func main() {
	type Row struct {
		Col0 bool   `db:"col0"`
		Col1 int64  `db:"col1"`
		Col2 string `db:"col2"`
	}

	fmt.Println(s.Reify(s.StructsInsertOf(
		Row{true, 10, `one`},
		Row{false, 20, `two`},
	)))
}
Output:

("col0", "col1", "col2") values ($1, $2, $3), ($4, $5, $6) [true 10 one false 20 two]

func StructsInsertOf added in v0.6.1

func StructsInsertOf[A any](val ...A) StructsInsert[A]

Shortcut for creating `StructsInsert` from the given values. Workaround for lack of type inference in type literals.

func (StructsInsert[A]) AppendExpr added in v0.6.1

func (self StructsInsert[A]) AppendExpr(text []byte, args []any) ([]byte, []any)

Implement the `Expr` interface, making this a sub-expression.

func (StructsInsert[_]) AppendTo added in v0.7.0

func (self StructsInsert[_]) AppendTo(text []byte) []byte

Implement the `AppenderTo` interface, sometimes allowing more efficient text encoding.

func (StructsInsert[_]) String added in v0.6.1

func (self StructsInsert[_]) String() string

Implement the `fmt.Stringer` interface for debug purposes.

type Table added in v0.2.0

type Table Identifier

Same as `Identifier`, but preceded by the word "table". The SQL clause "table some_name" is equivalent to "select * from some_name".

func (Table) AppendExpr added in v0.2.0

func (self Table) AppendExpr(text []byte, args []any) ([]byte, []any)

Implement the `Expr` interface, making this a sub-expression.

func (Table) AppendTo added in v0.7.0

func (self Table) AppendTo(text []byte) []byte

Implement the `AppenderTo` interface, sometimes allowing more efficient text encoding.

func (Table) String added in v0.2.0

func (self Table) String() string

Implement the `fmt.Stringer` interface for debug purposes.

type TagFilter added in v0.5.0

type TagFilter string

Implements `Filter` by requiring that the struct field has this specific tag. The tag's value for any given field is ignored, only its existence is checked.

func (TagFilter) AllowField added in v0.5.0

func (self TagFilter) AllowField(field r.StructField) bool

type Token added in v0.2.0

type Token struct {
	Text string
	Type TokenType
}

Represents an arbitrary chunk of SQL text parsed by `Tokenizer`.

func (Token) IsInvalid added in v0.2.0

func (self Token) IsInvalid() bool

True if the token's type is `TokenTypeInvalid`. This is used to detect end of iteration when calling `(*Tokenizer).Next`.

func (Token) ParseNamedParam added in v0.2.0

func (self Token) ParseNamedParam() NamedParam

Assumes that the token has `TokenTypeNamedParam` and looks like a Postgres-style named param: ":one", ":two" and so on. Parses and returns the parameter's name without the leading ":". Panics if the text had the wrong structure.

func (Token) ParseOrdinalParam added in v0.2.0

func (self Token) ParseOrdinalParam() OrdinalParam

Assumes that the token has `TokenTypeOrdinalParam` and looks like a Postgres-style ordinal param: "$1", "$2" and so on. Parses and returns the number. Panics if the text had the wrong structure.

func (Token) String added in v0.2.0

func (self Token) String() string

Implement `fmt.Stringer` for debug purposes.

type TokenType added in v0.2.0

type TokenType byte

Part of `Token`.

const (
	TokenTypeInvalid TokenType = iota
	TokenTypeText
	TokenTypeWhitespace
	TokenTypeQuotedSingle
	TokenTypeQuotedDouble
	TokenTypeQuotedGrave
	TokenTypeCommentLine
	TokenTypeCommentBlock
	TokenTypeDoubleColon
	TokenTypeOrdinalParam
	TokenTypeNamedParam
)

type Tokenizer added in v0.2.0

type Tokenizer struct {
	Source    string
	Transform func(Token) Token
	// contains filtered or unexported fields
}

Partial SQL tokenizer used internally by `(*Prep).Parse` to parse queries, in particular to convert named parameters into other expressions.

Goals:

  • Correctly parse whitespace, comments, quoted strings and identifiers, ordinal parameters, named parameters.

  • Decently fast and allocation-free tokenization.

Non-goals:

  • Full SQL parser.

Notable limitations:

  • No special support for dollar-quoted strings, which are rarely if ever used in dynamically-generated queries.

func (*Tokenizer) Next added in v0.2.0

func (self *Tokenizer) Next() Token

Returns the next token if possible. When the tokenizer reaches the end, this returns an empty `Token{}`. Call `Token.IsInvalid` to detect the end.

type Update added in v0.2.0

type Update UpdateVoid

Shortcut for simple `update A set B where C returning *` expressions. See the examples. Also see `UpdateVoid` which doesn't have `returning *`.

Example
package main

import (
	"fmt"

	s "github.com/mitranim/sqlb"
)

func main() {
	type Filter struct {
		Col0 int64 `db:"col0"`
		Col1 int64 `db:"col1"`
	}

	type Fields struct {
		Col2 int64 `db:"col2"`
		Col3 int64 `db:"col3"`
	}

	fmt.Println(s.Reify(
		s.Update{`some_table`, Filter{10, 20}, Fields{30, 40}},
	))

}
Output:

update "some_table" set "col2" = $1, "col3" = $2 where "col0" = $3 and "col1" = $4 returning * [30 40 10 20]

func (Update) AppendExpr added in v0.2.0

func (self Update) AppendExpr(text []byte, args []any) ([]byte, []any)

Implement the `Expr` interface, making this a sub-expression.

func (Update) AppendTo added in v0.7.0

func (self Update) AppendTo(text []byte) []byte

Implement the `AppenderTo` interface, sometimes allowing more efficient text encoding.

func (Update) String added in v0.2.0

func (self Update) String() string

Implement the `fmt.Stringer` interface for debug purposes.

type UpdateVoid added in v0.7.1

type UpdateVoid struct {
	What   Ident
	Where  any
	Fields any
}

Shortcut for simple `update A set B where C` expressions. Also see `Update` which appends `returning *`.

func (UpdateVoid) AppendExpr added in v0.7.1

func (self UpdateVoid) AppendExpr(text []byte, args []any) ([]byte, []any)

Implement the `Expr` interface, making this a sub-expression.

func (UpdateVoid) AppendTo added in v0.7.1

func (self UpdateVoid) AppendTo(text []byte) []byte

Implement the `AppenderTo` interface, sometimes allowing more efficient text encoding.

func (UpdateVoid) String added in v0.7.1

func (self UpdateVoid) String() string

Implement the `fmt.Stringer` interface for debug purposes.

type Upsert added in v0.6.5

type Upsert UpsertVoid

Same as `UpsertVoid` but also appends `returning *`.

func (Upsert) AppendExpr added in v0.6.5

func (self Upsert) AppendExpr(text []byte, args []any) ([]byte, []any)

Implement the `Expr` interface, making this a sub-expression.

func (Upsert) AppendTo added in v0.7.0

func (self Upsert) AppendTo(text []byte) []byte

Implement the `AppenderTo` interface, sometimes allowing more efficient text encoding.

func (Upsert) String added in v0.6.5

func (self Upsert) String() string

Implement the `fmt.Stringer` interface for debug purposes.

type UpsertConflict added in v0.7.3

type UpsertConflict UpsertConflictVoid

Same as `UpsertConflictVoid` but also appends `returning *`.

func (UpsertConflict) AppendExpr added in v0.7.3

func (self UpsertConflict) AppendExpr(text []byte, args []any) ([]byte, []any)

Implement the `Expr` interface, making this a sub-expression.

func (UpsertConflict) AppendTo added in v0.7.3

func (self UpsertConflict) AppendTo(text []byte) []byte

Implement the `AppenderTo` interface, sometimes allowing more efficient text encoding.

func (UpsertConflict) String added in v0.7.3

func (self UpsertConflict) String() string

Implement the `fmt.Stringer` interface for debug purposes.

type UpsertConflictVoid added in v0.7.3

type UpsertConflictVoid struct {
	What Ident
	Conf Str
	Cols any
}

Represents an SQL upsert query. Similar to `UpsertVoid` (see its comment / doc), but instead of generating the conflict clause from the provided "key" fields, requires the caller to provide a hardcoded conflict target string (the `.Conf` field). Useful for conflicts that involve partial indexes. Also see `UpsertConflict` which appends the `returning *` clause.

func (UpsertConflictVoid) AppendExpr added in v0.7.3

func (self UpsertConflictVoid) AppendExpr(text []byte, args []any) ([]byte, []any)

Implement the `Expr` interface, making this a sub-expression.

func (UpsertConflictVoid) AppendTo added in v0.7.3

func (self UpsertConflictVoid) AppendTo(text []byte) []byte

Implement the `AppenderTo` interface, sometimes allowing more efficient text encoding.

func (UpsertConflictVoid) String added in v0.7.3

func (self UpsertConflictVoid) String() string

Implement the `fmt.Stringer` interface for debug purposes.

type UpsertVoid added in v0.7.1

type UpsertVoid struct {
	What Ident
	Keys any
	Cols any
}

Represents an SQL upsert query like this:

insert into some_table
	(key_0, key_1, col_2, col_3)
values
	($1, $2, $3, $4)
on conflict (key_0, key_1)
do update set
	key_0 = excluded.key_0,
	key_1 = excluded.key_1,
	col_2 = excluded.col_2,
	col_3 = excluded.col_3

Notes:

  • `.Keys` must be a struct.
  • `.Keys` supports `Sparse` and may be empty.
  • When `.Keys` is empty, this is equivalent to the `Insert` type.
  • `.Cols` must be a struct.
  • `.Cols` supports `Sparse` and may be empty.
  • `.Keys` provides names and values for key columns which participate in the `on conflict` clause.
  • `.Cols` provides names and values for other columns.

Also see `Upsert` which appends the `returning *` clause.

func (UpsertVoid) AppendExpr added in v0.7.1

func (self UpsertVoid) AppendExpr(text []byte, args []any) ([]byte, []any)

Implement the `Expr` interface, making this a sub-expression.

func (UpsertVoid) AppendTo added in v0.7.1

func (self UpsertVoid) AppendTo(text []byte) []byte

Implement the `AppenderTo` interface, sometimes allowing more efficient text encoding.

func (UpsertVoid) String added in v0.7.1

func (self UpsertVoid) String() string

Implement the `fmt.Stringer` interface for debug purposes.

type Wrap added in v0.2.0

type Wrap struct {
	Prefix string
	Expr   Expr
	Suffix string
}

Combines an expression with a string prefix and suffix. If the expr is nil, this is a nop, and the prefix and suffix are ignored. Mostly an internal tool for building other expression types.

func (Wrap) AppendExpr added in v0.2.0

func (self Wrap) AppendExpr(text []byte, args []any) ([]byte, []any)

Difference from `Trio`: if the expr is nil, nothing is appended. Implement the `Expr` interface, making this a sub-expression.

func (Wrap) AppendTo added in v0.7.0

func (self Wrap) AppendTo(text []byte) []byte

Implement the `AppenderTo` interface, sometimes allowing more efficient text encoding.

func (Wrap) String added in v0.2.0

func (self Wrap) String() string

Implement the `fmt.Stringer` interface for debug purposes.

Jump to

Keyboard shortcuts

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