Documentation
¶
Overview ¶
Package drops is a Drizzle-inspired, driver-agnostic SQL toolkit for Go.
The root package defines the Driver, Tx, Rows and Result interfaces that adapt the toolkit to any underlying database connection (database/sql, pgx, or your own pool) plus the building blocks for composing SQL: Expression and Builder.
Observability is provided by Hook, ChainHooks, and CallHook — a single contract shared by every dialect. A ready-made structured hook is available via LoggerHook.
Dialect packages ¶
github.com/bernardoforcillo/drops/pg — PostgreSQL. Full surface: SELECT / INSERT / UPDATE / DELETE, DDL (schemas, enums, sequences, views, functions, triggers, indexes), file-based migrations, and eager-loaded relations (HasMany, HasOne, BelongsTo, ManyToMany, MorphTo, MorphMany). Includes opt-in ORM features: lifecycle hooks (OnInsert / OnUpdate / OnDelete), default scopes, typed Entity[T] with validators and optimistic locking.
github.com/bernardoforcillo/drops/clickhouse — ClickHouse. Typed columns (Array, Nullable, LowCardinality, Decimal, DateTime64, Tuple, Map, Enum8/16), full SELECT (PREWHERE, FINAL, SAMPLE, ASOF JOIN, SETTINGS), batch INSERT, and the analytics-aggregate library.
github.com/bernardoforcillo/drops/qdrant — Qdrant vector database. Stdlib-only HTTP client: collection management, upsert/delete/retrieve, search / recommend / scroll, and a Must/Should/MustNot filter DSL.
Cache packages ¶
github.com/bernardoforcillo/drops/cache — driver-agnostic cache interface (Get / Set / Delete / Exists / TTL / Ping / Close) plus a MultiCache batch extension and sentinel errors.
github.com/bernardoforcillo/drops/cache/memory — in-process LRU cache with TTL and an optional janitor goroutine. Zero deps; ideal for tests and the local tier of a two-level cache.
github.com/bernardoforcillo/drops/cache/redis — Redis backend with a minimal RESP2 client and a bounded connection pool. Zero deps.
Adapter ¶
- github.com/bernardoforcillo/drops/stdlib — wraps a *sql.DB as a drops.Driver so any database/sql driver works with drops out of the box.
Code generation ¶
- github.com/bernardoforcillo/drops/cmd/dropsgen — optional binary that emits zero-reflection bind/scan helpers from `//drops:entity`-annotated structs. Pair with `go:generate` to skip reflection on the hot row-binding path.
Index ¶
- Variables
- func BacktickQuoteIdent(name string) string
- func CallHook(h Hook, ctx context.Context, e QueryEvent)
- func InTx(ctx context.Context, d Driver, fn func(Tx) error) (err error)
- func ScanAll(rows Rows, dest any) error
- func ScanOne(rows Rows, dest any) error
- func StdQuoteIdent(name string) string
- func String(e Expression) (sql string, args []any)
- func StringWithDialect(d Dialect, e Expression) (sql string, args []any)
- func StructFields(t reflect.Type) map[string][]int
- type Builder
- func (b *Builder) AddArg(v any)
- func (b *Builder) Append(e Expression)
- func (b *Builder) AppendList(sep string, exprs []Expression)
- func (b *Builder) Dialect() Dialect
- func (b *Builder) SQL() (sql string, args []any)
- func (b *Builder) WriteByte(c byte) error
- func (b *Builder) WriteIdent(name string)
- func (b *Builder) WriteQualified(parts ...string)
- func (b *Builder) WriteString(s string)
- type BuilderOption
- type Dialect
- type Driver
- type ExprFunc
- type Expression
- type Hook
- type LoggerFunc
- type LoggerOptions
- type Param
- type QueryEvent
- type Raw
- type Result
- type Rows
- type Tx
Constants ¶
This section is empty.
Variables ¶
var ErrNoRows = errors.New("drops: no rows in result set")
ErrNoRows is returned by ScanOne when the result set is empty. It is the dialect-agnostic counterpart to pg.ErrNoRows / sqlite.ErrNoRows, which wrap it.
Functions ¶
func BacktickQuoteIdent ¶ added in v0.4.0
BacktickQuoteIdent quotes name with MySQL-style backticks.
func CallHook ¶
func CallHook(h Hook, ctx context.Context, e QueryEvent)
CallHook invokes h with (ctx, e), tolerating both a nil hook and a hook that panics. It is the safe entrypoint every dialect uses to emit events: a buggy user-supplied Hook (nil dereference, out-of-bounds index in a logger format string, etc.) must NOT take down the caller's request goroutine.
Adapters typically call this from their internal `emit` helper:
func (c *Cache) emit(ctx context.Context, e drops.QueryEvent) {
drops.CallHook(c.hook, ctx, e)
}
func InTx ¶
InTx runs fn inside a transaction opened on d. The transaction is committed if fn returns nil and rolled back otherwise (including on panic, after which the panic is re-raised).
Rollback uses a detached context with its own short timeout so a cancelled or expired caller-ctx doesn't prevent the cleanup from running.
func ScanAll ¶ added in v0.4.0
ScanAll consumes every row from rows into dest, a non-nil pointer to a slice of structs or *structs. Mapping rules are those of ScanOne.
func ScanOne ¶ added in v0.4.0
ScanOne consumes the first row from rows into dest (a non-nil pointer to a struct), returning ErrNoRows when the cursor is empty. It is the shared reflection scanner used by every SQL-like dialect, so the struct-to-column mapping rules are identical whichever backend a query ran against:
- a `drop:"col"` field tag names the column (`drop:"-"` skips the field; a comma-separated tag like `drop:"col,opt"` uses the part before the first comma);
- otherwise both the exported field name and its camelCase form match;
- embedded structs are walked;
- columns with no matching field are scanned into a discard sink.
func StdQuoteIdent ¶ added in v0.4.0
StdQuoteIdent quotes name with standard SQL double quotes, doubling embedded quotes. Exposed so dialect packages can build their QuoteIdent without re-implementing the escaping.
func String ¶
func String(e Expression) (sql string, args []any)
String renders an Expression to its SQL text and bound argument list using the default PostgreSQL syntax. Useful in tests and for logging.
func StringWithDialect ¶ added in v0.4.0
func StringWithDialect(d Dialect, e Expression) (sql string, args []any)
StringWithDialect renders an Expression using d's placeholder and identifier-quoting rules. It is the dialect-aware counterpart to String — dialect packages and their tests use it to render SQL the backend's way.
func StructFields ¶ added in v0.4.0
StructFields returns the column-name → field-index-path map the scanner uses: `drop:"col"` tags win (`drop:"-"` skips), otherwise the exported field name and its camelCase form both map, and embedded structs are walked. Dialect entity layers use it to bind struct fields to columns without re-implementing the reflection walk. The result is cached per type and must not be mutated.
Types ¶
type Builder ¶
type Builder struct {
// contains filtered or unexported fields
}
Builder accumulates SQL text and bound parameters. By default it uses PostgreSQL's $N numbered placeholders; dialects that need a different style (ClickHouse / MySQL use `?`) install one via WithPlaceholder.
A Builder is not safe for concurrent use; create one per query.
func NewBuilder ¶
func NewBuilder(opts ...BuilderOption) *Builder
NewBuilder returns an empty Builder.
func (*Builder) AddArg ¶
AddArg binds a value and writes its placeholder. The placeholder index is 1-based; the default rendering is "$<n>" (PostgreSQL) and can be overridden via WithPlaceholder.
func (*Builder) Append ¶
func (b *Builder) Append(e Expression)
Append writes an Expression into the Builder. nil is a no-op.
func (*Builder) AppendList ¶
func (b *Builder) AppendList(sep string, exprs []Expression)
AppendList writes a list of expressions separated by sep.
func (*Builder) Dialect ¶ added in v0.4.0
Dialect returns the Builder's installed dialect, or nil when it is using the default PostgreSQL syntax.
func (*Builder) WriteByte ¶
WriteByte appends a single raw byte of SQL. The returned error is always nil (strings.Builder.WriteByte never fails); the signature matches io.ByteWriter, which go vet's stdmethods check requires for a method named WriteByte. Callers ignore the error — errcheck is configured to skip this method, mirroring how it skips the identical (*strings.Builder).WriteByte by default.
func (*Builder) WriteIdent ¶
WriteIdent appends a quoted identifier. With no dialect installed it uses standard double quotes with "" doubling; a dialect can override the quoting (e.g. MySQL backticks) via QuoteIdent.
func (*Builder) WriteQualified ¶
WriteQualified appends a qualified identifier such as "schema"."table" or "table"."column". Empty parts are skipped.
func (*Builder) WriteString ¶
WriteString appends raw SQL. Callers must ensure it is safe (no unsanitised user input).
type BuilderOption ¶
type BuilderOption func(*Builder)
BuilderOption configures a Builder at construction time.
func WithDialect ¶ added in v0.4.0
func WithDialect(d Dialect) BuilderOption
WithDialect installs d so the Builder renders placeholders and quotes identifiers the dialect's way. It is the swap point for targeting a different SQL-like backend: the query builders are otherwise dialect-agnostic. Passing nil is a no-op (PostgreSQL defaults stay in force). WithDialect also sets the placeholder function from d.Placeholder unless a later WithPlaceholder overrides it.
func WithPlaceholder ¶
func WithPlaceholder(fn func(n int) string) BuilderOption
WithPlaceholder overrides how parameter placeholders are rendered. fn receives the 1-based index of the bound argument. The default emits "$<n>" (PostgreSQL); pass `func(int) string { return "?" }` for dialects (ClickHouse, MySQL) that use positional question marks.
type Dialect ¶ added in v0.4.0
type Dialect interface {
// Name identifies the dialect, e.g. "postgresql" or "sqlite".
Name() string
// Placeholder renders the bind marker for the n-th (1-based) bound
// argument — "$1" for PostgreSQL, "?" for SQLite/MySQL, "@p1" for
// SQL Server, and so on.
Placeholder(n int) string
// QuoteIdent quotes a single identifier, escaping the dialect's
// quote character. PostgreSQL and SQLite both use double quotes
// with "" doubling; MySQL uses backticks.
QuoteIdent(name string) string
// SupportsReturning reports whether INSERT/UPDATE/DELETE ...
// RETURNING is available. PostgreSQL and SQLite (>= 3.35) support
// it; MySQL does not. Builders consult this to decide whether a
// RETURNING clause can be emitted or must be emulated.
SupportsReturning() bool
}
Dialect abstracts the SQL-syntax differences between database backends so the same schema and query builders can target any SQL-like connector by swapping the Dialect a Builder carries.
A Builder with no Dialect installed behaves exactly as it always has: PostgreSQL-style "$N" placeholders and standard double-quoted identifiers. Installing a Dialect (via WithDialect) reroutes placeholder rendering and identifier quoting through it, so pointing the same query code at SQLite instead of PostgreSQL is a matter of swapping the Dialect and the underlying Driver — nothing else in the builder chain changes.
Implementations must be safe for concurrent use: a Dialect is shared across every Builder a DB creates.
type Driver ¶
type Driver interface {
// Exec runs a statement that does not return rows.
Exec(ctx context.Context, sql string, args ...any) (Result, error)
// Query runs a statement that returns rows.
Query(ctx context.Context, sql string, args ...any) (Rows, error)
// Begin opens a transaction.
Begin(ctx context.Context) (Tx, error)
}
Driver is the minimal contract a database connection must satisfy to be used with drops. Any pgx, database/sql or custom connection can be wrapped to implement it; drops itself imports no concrete driver.
type Expression ¶
type Expression interface {
WriteSQL(b *Builder)
}
Expression is anything that can render itself into a Builder. Every fragment of a query — a column reference, an operator, a subquery — implements it.
Expressions are stateless and safe to reuse across goroutines. The Builder they write into is not.
type Hook ¶
type Hook func(ctx context.Context, e QueryEvent)
Hook observes driver operations performed via a DB. It is purely observational — the operation has already happened by the time the hook is invoked, and the hook's return value (if any) is discarded.
Hooks must be safe for concurrent use: a single DB may issue queries from multiple goroutines, and each will invoke the hook independently.
To compose multiple hooks, use ChainHooks.
func ChainHooks ¶
ChainHooks returns a Hook that invokes each given hook in order. nil hooks are skipped. A panic inside any hook is recovered so the remaining hooks still run and the caller of CallHook is never crashed by a buggy observer.
func LoggerHook ¶
func LoggerHook(log LoggerFunc, opts ...LoggerOptions) Hook
LoggerHook returns a Hook that writes one line per operation to log. It works with any DB that accepts a drops.Hook — pg, clickhouse, qdrant — so a single function shape covers every dialect.
db := pg.New(stdlib.New(sqlDB)).WithHook(
drops.LoggerHook(log.Printf,
drops.LoggerOptions{SlowQuery: 100 * time.Millisecond}),
)
Pair with ChainHooks to combine logging with metrics or tracing.
type LoggerFunc ¶
LoggerFunc is the minimal logger contract used by LoggerHook. Most production logger types — log/slog's printf-style helpers, zerolog, zap.SugaredLogger, even fmt.Printf — satisfy it directly or via a one-line adapter, so the hook itself takes no dependency on any specific logging library.
type LoggerOptions ¶
type LoggerOptions struct {
// SlowQuery, if non-zero, limits logging to operations that took at
// least this long. Errors always log, regardless of this threshold.
SlowQuery time.Duration
// LogArgs controls whether bound parameters are included in log
// lines. Default false because args may contain secrets.
LogArgs bool
// MaxSQLLength truncates the SQL fragment to this many characters.
// 0 means no truncation.
MaxSQLLength int
// Redact, if non-nil, is applied to a copy of the args slice before
// logging. Use it to strip passwords, tokens, PII, etc. when
// LogArgs is true. Ignored when LogArgs is false (no args are
// logged at all).
Redact func(args []any) []any
}
LoggerOptions tunes LoggerHook behaviour. The zero value enables query logging for every event.
type QueryEvent ¶
type QueryEvent struct {
// Kind names the operation: "exec", "query", "begin", "commit",
// "rollback", "ping".
Kind string
// SQL is the rendered statement text. Empty for begin/commit/
// rollback/ping.
SQL string
// Args are the bound parameters, in $1, $2, ... order. They may
// contain secrets — redact before logging in untrusted contexts.
Args []any
// Duration is the elapsed time of the operation.
Duration time.Duration
// Err is the error returned by the operation, or nil on success.
Err error
}
QueryEvent carries observability information about a single driver operation. It is passed to a Hook after the operation completes (or fails) so the hook can log, trace, emit metrics, etc. without altering control flow.
type Raw ¶
type Raw string
Raw is an Expression containing pre-formed SQL text. Use sparingly — values inside Raw are not parameter-checked; for parameterised fragments, compose Param or ExprFunc instead.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
_examples
|
|
|
postgres
command
Example program demonstrating the drops/pg package against a real PostgreSQL instance via the database/sql adapter.
|
Example program demonstrating the drops/pg package against a real PostgreSQL instance via the database/sql adapter. |
|
Package cache defines a small, driver-agnostic Cache interface and the supporting hook / error contract.
|
Package cache defines a small, driver-agnostic Cache interface and the supporting hook / error contract. |
|
memory
Package memory provides an in-process cache.Cache implementation.
|
Package memory provides an in-process cache.Cache implementation. |
|
redis
Package redis provides a cache.Cache implementation backed by a Redis server.
|
Package redis provides a cache.Cache implementation backed by a Redis server. |
|
Package clickhouse provides a ClickHouse dialect for drops.
|
Package clickhouse provides a ClickHouse dialect for drops. |
|
cmd
|
|
|
drops
command
drops is the schema toolkit CLI.
|
drops is the schema toolkit CLI. |
|
dropsgen
command
dropsgen generates zero-reflection bind / scan helpers for structs tagged as drops entities.
|
dropsgen generates zero-reflection bind / scan helpers for structs tagged as drops entities. |
|
examples
|
|
|
generate
command
generate demonstrates drops's drizzle-kit-compatible migration generator.
|
generate demonstrates drops's drizzle-kit-compatible migration generator. |
|
sqlgen
command
sqlgen prints the SQL produced by the drops/pg builders.
|
sqlgen prints the SQL produced by the drops/pg builders. |
|
Package pg provides PostgreSQL schema declarations and a fluent query builder for use with drops.
|
Package pg provides PostgreSQL schema declarations and a fluent query builder for use with drops. |
|
Package qdrant is a focused HTTP client for the Qdrant vector database (https://qdrant.tech).
|
Package qdrant is a focused HTTP client for the Qdrant vector database (https://qdrant.tech). |
|
Package sqlite is the SQLite dialect for drops.
|
Package sqlite is the SQLite dialect for drops. |
|
Package stdlib adapts a *database/sql.DB to drops.Driver.
|
Package stdlib adapts a *database/sql.DB to drops.Driver. |