Documentation
¶
Overview ¶
Package clickhouse provides a ClickHouse dialect for drops.
The shape mirrors the drops/pg package — typed columns via Col[T], table declarations, fluent query builders, and a DB wrapper with the same Hook / Ping / Close / InTx surface — but the SQL it emits is ClickHouse-flavoured:
- "?" positional placeholders (matches the clickhouse-go database/sql driver)
- Engine-bound tables (CREATE TABLE … ENGINE = MergeTree() ORDER BY …)
- Native CH types: Array(T), Nullable(T), LowCardinality(T), Decimal(P,S), Map(K,V), Tuple(...), Enum8/16, DateTime[64], fixed-width integers (UInt8/UInt64/Int8/Int64/…), UUID
- Aggregates such as uniq, uniqExact, quantile, anyAgg
Driver-agnostic: like drops/pg, the package imports no concrete driver. The bundled drops/stdlib adapter works against any database/ sql-compatible ClickHouse driver — clickhouse-go's stdlib bridge is the standard choice:
import (
_ "github.com/ClickHouse/clickhouse-go/v2"
"github.com/bernardoforcillo/drops/clickhouse"
"github.com/bernardoforcillo/drops/stdlib"
)
sqlDB, _ := sql.Open("clickhouse", "clickhouse://localhost:9000/default")
db := clickhouse.New(stdlib.New(sqlDB))
Templates (Timestamps, SoftDelete, Audit, UUIDPrimaryKey) provide reusable column groups — see template.go for the function-style pattern and mixin.go for the richer Mixin interface. ClickHouse has no foreign keys, so Audit emits plain scalar columns mirroring the target's type. Lifecycle hooks are limited to OnInsert (no builder-side UPDATE/DELETE); default filters on SelectBuilder honour Unscoped() for opt-out.
Entity[T] (see entity.go) binds a Go struct to a Table and exposes Create / CreateMany / Query — the narrow subset of CRUD that maps to ClickHouse's Insert + Select builders. Entity.Validate registers per-row validators that run before Create / CreateMany; on CreateMany the first failing row aborts the whole batch.
What this package does NOT try to mirror from drops/pg:
- per-row UPDATE/DELETE (ClickHouse mutations are asynchronous and fundamentally different; use ALTER TABLE … UPDATE/DELETE via raw SQL when you need them)
- ON CONFLICT (handled by engine choice, e.g. ReplacingMergeTree)
- Foreign keys / referential integrity (ClickHouse has none)
- Schema introspection and Push (planned)
Index ¶
- Variables
- func Abs(e any) drops.Expression
- func And(preds ...drops.Expression) drops.Expression
- func AnyAgg(e drops.Expression) drops.Expression
- func AnyHeavy(e drops.Expression) drops.Expression
- func AnyLast(e drops.Expression) drops.Expression
- func ArgMax(value, by drops.Expression) drops.Expression
- func ArgMin(value, by drops.Expression) drops.Expression
- func As(e drops.Expression, alias string) drops.Expression
- func Avg(e drops.Expression) drops.Expression
- func Between(left, low, high any) drops.Expression
- func Cast(e any, typeSQL string) drops.Expression
- func CastAs(e any, typeSQL string) drops.Expression
- func Coalesce(args ...any) drops.Expression
- func Count(e drops.Expression) drops.Expression
- func CountAll() drops.Expression
- func CreateDatabase(name string) drops.Expression
- func CreateDatabaseIfNotExists(name string) drops.Expression
- func CreateTable(t *Table) drops.Expression
- func CreateTableErr(t *Table) (drops.Expression, error)
- func CreateTableIfNotExists(t *Table) drops.Expression
- func DateDiff(unit string, a, b any) drops.Expression
- func DenseRank() drops.Expression
- func DropDatabase(name string) drops.Expression
- func DropDatabaseIfExists(name string) drops.Expression
- func DropTable(t *Table) drops.Expression
- func DropTableIfExists(t *Table) drops.Expression
- func Eq(left, right any) drops.Expression
- func Exists(q drops.Expression) drops.Expression
- func FirstValue(expr any) drops.Expression
- func Func(name string, args ...any) drops.Expression
- func GroupArray(e drops.Expression) drops.Expression
- func GroupUniqArray(e drops.Expression) drops.Expression
- func Gt(left, right any) drops.Expression
- func Gte(left, right any) drops.Expression
- func ILike(left, pattern any) drops.Expression
- func IfNull(e, fallback any) drops.Expression
- func In(left any, values ...any) drops.Expression
- func InSub(value any, sub drops.Expression) drops.Expression
- func IsNotNull(e any) drops.Expression
- func IsNull(e any) drops.Expression
- func Lag(expr any, args ...any) drops.Expression
- func LastValue(expr any) drops.Expression
- func Lead(expr any, args ...any) drops.Expression
- func Length(e any) drops.Expression
- func Like(left, pattern any) drops.Expression
- func LoggerHook(log LoggerFunc, opts ...LoggerOptions) drops.Hook
- func Lower(e drops.Expression) drops.Expression
- func Lt(left, right any) drops.Expression
- func Lte(left, right any) drops.Expression
- func Max(e drops.Expression) drops.Expression
- func Min(e drops.Expression) drops.Expression
- func Ne(left, right any) drops.Expression
- func Not(p drops.Expression) drops.Expression
- func NotExists(q drops.Expression) drops.Expression
- func NotIn(left any, values ...any) drops.Expression
- func NotInSub(value any, sub drops.Expression) drops.Expression
- func Now() drops.Expression
- func NthValue(expr, n any) drops.Expression
- func OptimizeTable(t *Table, final bool) drops.Expression
- func Or(preds ...drops.Expression) drops.Expression
- func Over(fn drops.Expression, win *Window) drops.Expression
- func Quantile(level float64, e drops.Expression) drops.Expression
- func QuantileExact(level float64, e drops.Expression) drops.Expression
- func QuantileTiming(level float64, e drops.Expression) drops.Expression
- func Rank() drops.Expression
- func Round(e any) drops.Expression
- func RowNumber() drops.Expression
- func Subquery(q drops.Expression) drops.Expression
- func Sum(e drops.Expression) drops.Expression
- func ToDate(e any) drops.Expression
- func ToDateTime(e any) drops.Expression
- func ToSQL(e drops.Expression) (sql string, args []any)
- func ToStartOfDay(e any) drops.Expression
- func ToStartOfHour(e any) drops.Expression
- func ToStartOfMinute(e any) drops.Expression
- func ToStartOfMonth(e any) drops.Expression
- func ToYYYYMM(e any) drops.Expression
- func ToYYYYMMDD(e any) drops.Expression
- func TruncateTable(t *Table) drops.Expression
- func TruncateTableIfExists(t *Table) drops.Expression
- func Uniq(e drops.Expression) drops.Expression
- func UniqExact(e drops.Expression) drops.Expression
- func UniqHLL12(e drops.Expression) drops.Expression
- func Upper(e drops.Expression) drops.Expression
- type AggregateSnapshot
- type AuditCols
- type AuditMixin
- type CTE
- type CaseExpr
- type Col
- func Add[T any](t *Table, c *Col[T]) *Col[T]
- func Bool(name string) *Col[bool]
- func Custom[T any](name, typeSQL string) *Col[T]
- func Date(name string) *Col[time.Time]
- func Date32(name string) *Col[time.Time]
- func DateTime(name, tz string) *Col[time.Time]
- func DateTime64(name string, precision int, tz string) *Col[time.Time]
- func Decimal(name string, precision, scale int) *Col[string]
- func FixedString(name string, n int) *Col[string]
- func Float32(name string) *Col[float32]
- func Float64(name string) *Col[float64]
- func Int8(name string) *Col[int8]
- func Int16(name string) *Col[int16]
- func Int32(name string) *Col[int32]
- func Int64(name string) *Col[int64]
- func JSON(name string) *Col[json.RawMessage]
- func String(name string) *Col[string]
- func UInt8(name string) *Col[uint8]
- func UInt16(name string) *Col[uint16]
- func UInt32(name string) *Col[uint32]
- func UInt64(name string) *Col[uint64]
- func UUID(name string) *Col[string]
- func (c *Col[T]) Between(lo, hi T) drops.Expression
- func (c *Col[T]) Codec(spec string) *Col[T]
- func (c *Col[T]) Comment(text string) *Col[T]
- func (c *Col[T]) Default(sqlExpr string) *Col[T]
- func (c *Col[T]) Eq(v T) drops.Expression
- func (c *Col[T]) EqCol(o *Col[T]) drops.Expression
- func (c *Col[T]) Expr(e drops.Expression) ColumnValue
- func (c *Col[T]) Gt(v T) drops.Expression
- func (c *Col[T]) GtCol(o *Col[T]) drops.Expression
- func (c *Col[T]) Gte(v T) drops.Expression
- func (c *Col[T]) GteCol(o *Col[T]) drops.Expression
- func (c *Col[T]) ILike(pattern string) drops.Expression
- func (c *Col[T]) In(values ...T) drops.Expression
- func (c *Col[T]) IsNotNull() drops.Expression
- func (c *Col[T]) IsNull() drops.Expression
- func (c *Col[T]) Like(pattern string) drops.Expression
- func (c *Col[T]) LowCardinality() *Col[T]
- func (c *Col[T]) Lt(v T) drops.Expression
- func (c *Col[T]) LtCol(o *Col[T]) drops.Expression
- func (c *Col[T]) Lte(v T) drops.Expression
- func (c *Col[T]) LteCol(o *Col[T]) drops.Expression
- func (c *Col[T]) Ne(v T) drops.Expression
- func (c *Col[T]) NeCol(o *Col[T]) drops.Expression
- func (c *Col[T]) NotIn(values ...T) drops.Expression
- func (c *Col[T]) Nullable() *Col[T]
- func (c *Col[T]) TTL(expr string) *Col[T]
- func (c *Col[T]) Val(v T) ColumnValue
- type ColRef
- type Column
- func (c *Column) As(alias string) drops.Expression
- func (c *Column) Asc() drops.Expression
- func (c *Column) Codec() string
- func (c *Column) Comment() string
- func (c *Column) DefaultSQL() string
- func (c *Column) Desc() drops.Expression
- func (c *Column) HasDefault() bool
- func (c *Column) IsNullable() bool
- func (c *Column) Name() string
- func (c *Column) TTL() string
- func (c *Column) Table() *Table
- func (c *Column) Type() ColumnType
- func (c *Column) WriteSQL(b *drops.Builder)
- type ColumnType
- func TypeArray(inner ColumnType) ColumnType
- func TypeEnum8(values map[string]int8) ColumnType
- func TypeEnum16(values map[string]int16) ColumnType
- func TypeLowCardinality(inner ColumnType) ColumnType
- func TypeMap(key, value ColumnType) ColumnType
- func TypeNullable(inner ColumnType) ColumnType
- func TypeTuple(members ...ColumnType) ColumnType
- type ColumnValue
- type Cursor
- type CursorSpec
- type DB
- func (db *DB) Begin(ctx context.Context) (*DB, drops.Tx, error)
- func (db *DB) Close() error
- func (db *DB) Driver() drops.Driver
- func (db *DB) Exec(ctx context.Context, sql string, args ...any) (drops.Result, error)
- func (db *DB) ExecExpr(ctx context.Context, e drops.Expression) (drops.Result, error)
- func (db *DB) Hook() drops.Hook
- func (db *DB) InTx(ctx context.Context, fn func(*DB) error) (err error)
- func (db *DB) Insert(t *Table) *InsertBuilder
- func (db *DB) Ping(ctx context.Context) error
- func (db *DB) Query(ctx context.Context, sql string, args ...any) (drops.Rows, error)
- func (db *DB) Select(cols ...drops.Expression) *SelectBuilder
- func (db *DB) WithHook(hook drops.Hook) *DB
- type Engine
- func AggregatingMergeTree() Engine
- func CollapsingMergeTree(signCol string) Engine
- func Log() Engine
- func Memory() Engine
- func MergeTree() Engine
- func Null() Engine
- func Raw(text string) Engine
- func ReplacingMergeTree(versionCol string) Engine
- func ReplicatedMergeTree(zkPath, replica string) Engine
- func StripeLog() Engine
- func SummingMergeTree(cols ...string) Engine
- func TinyLog() Engine
- func VersionedCollapsingMergeTree(signCol, versionCol string) Engine
- type Entity
- func (e *Entity[T]) Create(db *DB, ctx context.Context, r *T) (drops.Result, error)
- func (e *Entity[T]) CreateMany(db *DB, ctx context.Context, rs []T) (drops.Result, error)
- func (e *Entity[T]) Query(db *DB) *EntityQuery[T]
- func (e *Entity[T]) Table() *Table
- func (e *Entity[T]) Validate(v Validator[T]) *Entity[T]
- type EntityQuery
- func (q *EntityQuery[T]) All(ctx context.Context) ([]T, error)
- func (q *EntityQuery[T]) Limit(n int64) *EntityQuery[T]
- func (q *EntityQuery[T]) Offset(n int64) *EntityQuery[T]
- func (q *EntityQuery[T]) One(ctx context.Context) (T, error)
- func (q *EntityQuery[T]) OrderBy(exprs ...drops.Expression) *EntityQuery[T]
- func (q *EntityQuery[T]) Prewhere(preds ...drops.Expression) *EntityQuery[T]
- func (q *EntityQuery[T]) Unscoped() *EntityQuery[T]
- func (q *EntityQuery[T]) Where(preds ...drops.Expression) *EntityQuery[T]
- type EnvelopeCipher
- type Event
- type EventInput
- type EventStore
- func (s *EventStore) Append(ctx context.Context, aggregateType, aggregateID string, expectedVersion int64, ...) error
- func (s *EventStore) LatestVersion(ctx context.Context, aggregateType, aggregateID string) (int64, error)
- func (s *EventStore) Load(ctx context.Context, aggregateType, aggregateID string, fromVersion int64) ([]Event, error)
- func (s *EventStore) LoadSnapshot(ctx context.Context, table, aggregateType, aggregateID string) (AggregateSnapshot, bool, error)
- func (s *EventStore) SaveSnapshot(ctx context.Context, table string, snap AggregateSnapshot) error
- func (s *EventStore) Stream(ctx context.Context, fromOffset int64, limit int) ([]Event, error)
- type Factory
- func (f *Factory[T]) Build() T
- func (f *Factory[T]) BuildN(n int) []T
- func (f *Factory[T]) Create(ctx context.Context, db *DB) (T, error)
- func (f *Factory[T]) CreateN(ctx context.Context, db *DB, n int) ([]T, error)
- func (f *Factory[T]) Entity() *Entity[T]
- func (f *Factory[T]) Reset()
- func (f *Factory[T]) Sequence() int
- func (f *Factory[T]) With(mutate func(*T)) *Factory[T]
- type InsertBuilder
- func (i *InsertBuilder) Columns(cols ...ColRef) *InsertBuilder
- func (i *InsertBuilder) Exec(ctx context.Context) (drops.Result, error)
- func (i *InsertBuilder) Row(values ...ColumnValue) *InsertBuilder
- func (i *InsertBuilder) Rows(rows ...[]ColumnValue) *InsertBuilder
- func (i *InsertBuilder) ToSQL() (sql string, args []any)
- func (i *InsertBuilder) WriteSQL(b *drops.Builder)
- type InsertHook
- type InsertHookCtx
- type InsertHookFunc
- type KMS
- type LocalKMS
- type LoggerFunc
- type LoggerOptions
- type MatView
- type MatViewManager
- func (m *MatViewManager) Add(v MatView) *MatViewManager
- func (m *MatViewManager) LastRefresh(name string) time.Time
- func (m *MatViewManager) Refresh(ctx context.Context, name string) error
- func (m *MatViewManager) RefreshAll(ctx context.Context) error
- func (m *MatViewManager) RefreshDownstream(ctx context.Context, upstream string) error
- func (m *MatViewManager) Start(ctx context.Context) error
- func (m *MatViewManager) Views() []MatView
- func (m *MatViewManager) WithPollInterval(d time.Duration) *MatViewManager
- type Mixin
- type MixinFunc
- type NullsOrdering
- type OrderKey
- type PlanStep
- type SelectBuilder
- func (s *SelectBuilder) AfterCursor(spec CursorSpec, c Cursor) *SelectBuilder
- func (s *SelectBuilder) All(ctx context.Context, dest any) error
- func (s *SelectBuilder) AllJoin(t *Table, on drops.Expression) *SelectBuilder
- func (s *SelectBuilder) AnyJoin(t *Table, on drops.Expression) *SelectBuilder
- func (s *SelectBuilder) AsofJoin(t *Table, on drops.Expression) *SelectBuilder
- func (s *SelectBuilder) BeforeCursor(spec CursorSpec, c Cursor) *SelectBuilder
- func (s *SelectBuilder) Count(ctx context.Context) (int64, error)
- func (s *SelectBuilder) Distinct() *SelectBuilder
- func (s *SelectBuilder) Final() *SelectBuilder
- func (s *SelectBuilder) From(t *Table) *SelectBuilder
- func (s *SelectBuilder) FullJoin(t *Table, on drops.Expression) *SelectBuilder
- func (s *SelectBuilder) GroupBy(exprs ...drops.Expression) *SelectBuilder
- func (s *SelectBuilder) Having(preds ...drops.Expression) *SelectBuilder
- func (s *SelectBuilder) Join(t *Table, on drops.Expression) *SelectBuilder
- func (s *SelectBuilder) LeftJoin(t *Table, on drops.Expression) *SelectBuilder
- func (s *SelectBuilder) Limit(n int64) *SelectBuilder
- func (s *SelectBuilder) Offset(n int64) *SelectBuilder
- func (s *SelectBuilder) One(ctx context.Context, dest any) error
- func (s *SelectBuilder) OrderBy(exprs ...drops.Expression) *SelectBuilder
- func (s *SelectBuilder) OrderByCursor(spec CursorSpec) *SelectBuilder
- func (s *SelectBuilder) Prewhere(preds ...drops.Expression) *SelectBuilder
- func (s *SelectBuilder) RightJoin(t *Table, on drops.Expression) *SelectBuilder
- func (s *SelectBuilder) Rows(ctx context.Context) (drops.Rows, error)
- func (s *SelectBuilder) SampleBy(e any) *SelectBuilder
- func (s *SelectBuilder) Setting(key, value string) *SelectBuilder
- func (s *SelectBuilder) ToSQL() (sql string, args []any)
- func (s *SelectBuilder) Unscoped() *SelectBuilder
- func (s *SelectBuilder) Where(preds ...drops.Expression) *SelectBuilder
- func (s *SelectBuilder) With(ctes ...*CTE) *SelectBuilder
- func (s *SelectBuilder) WriteSQL(b *drops.Builder)
- type SoftDeleteCols
- type SoftDeleteMixin
- type Table
- func (t *Table) Alias() string
- func (t *Table) As(alias string) *Table
- func (t *Table) Col(name string) *Column
- func (t *Table) Columns() []*Column
- func (t *Table) Database() string
- func (t *Table) DefaultFilter(e drops.Expression) *Table
- func (t *Table) Engine(e Engine) *Table
- func (t *Table) Name() string
- func (t *Table) OnInsert(h InsertHook) *Table
- func (t *Table) OrderBy(cols ...ColRef) *Table
- func (t *Table) PartitionBy(exprs ...drops.Expression) *Table
- func (t *Table) PrimaryKey(cols ...ColRef) *Table
- func (t *Table) SampleBy(e drops.Expression) *Table
- func (t *Table) Setting(key, value string) *Table
- func (t *Table) TTL(expr string) *Table
- func (t *Table) WriteSQL(b *drops.Builder)
- type TimestampsCols
- type TimestampsMixin
- type TreeMigration
- type TreeMigrator
- func (m *TreeMigrator) Add(mig TreeMigration) *TreeMigrator
- func (m *TreeMigrator) AddFS(fsys fs.FS, dir string) error
- func (m *TreeMigrator) AddSQL(id, name, branch string, parents []string, upSQL, downSQL string) *TreeMigrator
- func (m *TreeMigrator) Branches() []string
- func (m *TreeMigrator) Checkout(ctx context.Context, id string) error
- func (m *TreeMigrator) Down(ctx context.Context) error
- func (m *TreeMigrator) DownTo(ctx context.Context, id string) error
- func (m *TreeMigrator) Heads(ctx context.Context) ([]TreeStatus, error)
- func (m *TreeMigrator) Plan(ctx context.Context) ([]PlanStep, error)
- func (m *TreeMigrator) Status(ctx context.Context) ([]TreeStatus, error)
- func (m *TreeMigrator) Up(ctx context.Context) error
- func (m *TreeMigrator) UpTo(ctx context.Context, id string) error
- func (m *TreeMigrator) Validate() error
- func (m *TreeMigrator) WithDatabase(database string) *TreeMigrator
- type TreeStatus
- type UUIDPrimaryKeyCols
- type UUIDPrimaryKeyMixin
- type Validator
- type Window
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ( ErrNoRows = errors.New("drops/clickhouse: no rows in result set") ErrNoRowsToInsert = errors.New("drops/clickhouse: INSERT with no rows") ErrReturningUnsupported = errors.New("drops/clickhouse: RETURNING is not supported by ClickHouse") )
Sentinel errors for assertable failure modes.
var ( ErrTreeCycleDetected = errors.New("drops/clickhouse: migration DAG has a cycle") ErrTreeMissingParent = errors.New("drops/clickhouse: migration references unknown parent") ErrTreeIrreversible = errors.New("drops/clickhouse: migration has no Down (irreversible)") ErrTreeNoMigrationsApplied = errors.New("drops/clickhouse: no tree migrations applied") ErrTreeUnknownMigration = errors.New("drops/clickhouse: unknown migration ID") // ErrTreeMigrationTampered is returned when an already-applied // migration's SQL checksum no longer matches what was recorded at // apply time. ErrTreeMigrationTampered = errors.New("drops/clickhouse: applied migration SQL has changed — investigate before proceeding") )
Sentinel errors.
var ErrCircularDependency = errors.New("drops/clickhouse: MatViewManager: circular dependency")
ErrCircularDependency is returned by topological helpers when a cycle is detected.
var ErrConcurrencyConflict = errors.New("drops/clickhouse: event store concurrency conflict")
ErrConcurrencyConflict is returned by Append when the expectedVersion no longer matches the stream's head — another writer beat us to that version. Wrap your callsite in a retry loop that re-reads the stream before retrying.
Note: unlike the PostgreSQL variant this check is advisory (not database-enforced), so it catches concurrent writers only under normal single-writer-per-aggregate usage.
var ErrEngineRequired = errors.New("drops/clickhouse: table has no Engine set; call .Engine(clickhouse.MergeTree()) before CreateTable")
ErrEngineRequired is returned by CreateTable when the target table doesn't yet have an Engine set. ClickHouse refuses CREATE TABLE without an ENGINE clause, so we fail fast and clearly.
var ErrInvalidIdentifier = errors.New("drops/clickhouse: invalid SQL identifier")
ErrInvalidIdentifier is returned when a table / database / column name fails validation. The decorating wrappers use fmt.Errorf with %w so errors.Is(err, ErrInvalidIdentifier) succeeds.
var Placeholder = drops.WithPlaceholder(func(int) string { return "?" })
Placeholder is the placeholder strategy used by the ClickHouse dialect — bare positional question marks, matching the clickhouse-go database/sql driver.
Functions ¶
func Abs ¶
func Abs(e any) drops.Expression
func And ¶
func And(preds ...drops.Expression) drops.Expression
func AnyAgg ¶
func AnyAgg(e drops.Expression) drops.Expression
AnyAgg returns an arbitrary value from the group (CH's `any`). Named AnyAgg to avoid colliding with Go's any keyword visually.
func AnyHeavy ¶
func AnyHeavy(e drops.Expression) drops.Expression
func ArgMax ¶
func ArgMax(value, by drops.Expression) drops.Expression
Argument-aware aggregates: argMax / argMin.
func ArgMin ¶
func ArgMin(value, by drops.Expression) drops.Expression
func As ¶
func As(e drops.Expression, alias string) drops.Expression
As renames an expression (for SELECT projections).
func Avg ¶
func Avg(e drops.Expression) drops.Expression
func Between ¶
func Between(left, low, high any) drops.Expression
func Cast ¶ added in v0.5.0
func Cast(e any, typeSQL string) drops.Expression
Cast is an alias for CastAs, provided for source-level parity with the other dialects.
func CastAs ¶ added in v0.5.0
func CastAs(e any, typeSQL string) drops.Expression
CastAs renders CAST(<e> AS <type>) — ClickHouse's standard-SQL type conversion. typeSQL is a ClickHouse type name such as "UInt32", "String" or "DateTime". ClickHouse also accepts the toType() function family (toInt32, toString, …); use Func for those.
func Coalesce ¶
func Coalesce(args ...any) drops.Expression
func Count ¶
func Count(e drops.Expression) drops.Expression
func CountAll ¶
func CountAll() drops.Expression
func CreateDatabase ¶
func CreateDatabase(name string) drops.Expression
func CreateDatabaseIfNotExists ¶
func CreateDatabaseIfNotExists(name string) drops.Expression
func CreateTable ¶
func CreateTable(t *Table) drops.Expression
CreateTable returns a CREATE TABLE statement for t. It panics-via- expression: rendering builds a SQL fragment that ends up emitting a clearly-marked error string if the engine is missing, so a caller who forgets gets a loud failure at exec time rather than silent bad DDL. Use CreateTableErr if you want the engine check at build time.
Example ¶
ExampleCreateTable shows the rendered DDL for a MergeTree table.
package main
import (
"fmt"
"github.com/bernardoforcillo/drops/clickhouse"
)
// Schema fixtures used by the godoc examples — picked from a small
// analytics-style table so the rendered SQL is recognisable.
var exEvents = clickhouse.NewTable("events")
func main() {
sql, _ := clickhouse.ToSQL(clickhouse.CreateTableIfNotExists(exEvents))
// trim for godoc readability — only the first two clauses
end := 0
for n := 0; n < 2; n++ {
end = nthIndex(sql, "\n", end+1)
if end < 0 {
break
}
}
fmt.Println(sql[:end])
}
// nthIndex returns the index of the nth occurrence of sep in s
// starting from start, or -1.
func nthIndex(s, sep string, start int) int {
for i := start; i < len(s); i++ {
if s[i] == sep[0] {
return i
}
}
return -1
}
Output: CREATE TABLE IF NOT EXISTS "events" ( "id" UUID,
func CreateTableErr ¶
func CreateTableErr(t *Table) (drops.Expression, error)
CreateTableErr returns the DDL or ErrEngineRequired. Use it in migration tooling that wants a definite error rather than SQL that references a sentinel string.
func CreateTableIfNotExists ¶
func CreateTableIfNotExists(t *Table) drops.Expression
CreateTableIfNotExists is the IF NOT EXISTS variant.
func DenseRank ¶ added in v0.5.0
func DenseRank() drops.Expression
func DropDatabase ¶
func DropDatabase(name string) drops.Expression
func DropDatabaseIfExists ¶
func DropDatabaseIfExists(name string) drops.Expression
func DropTable ¶
func DropTable(t *Table) drops.Expression
func DropTableIfExists ¶
func DropTableIfExists(t *Table) drops.Expression
func Eq ¶
func Eq(left, right any) drops.Expression
func Exists ¶ added in v0.5.0
func Exists(q drops.Expression) drops.Expression
Exists renders EXISTS (<subquery>).
func FirstValue ¶ added in v0.5.0
func FirstValue(expr any) drops.Expression
func Func ¶
func Func(name string, args ...any) drops.Expression
Func is the escape hatch for any function not covered by helpers.
func GroupArray ¶
func GroupArray(e drops.Expression) drops.Expression
GroupArray returns an array of all values in the group.
func GroupUniqArray ¶
func GroupUniqArray(e drops.Expression) drops.Expression
GroupUniqArray is the deduplicated variant.
func Gt ¶
func Gt(left, right any) drops.Expression
func Gte ¶
func Gte(left, right any) drops.Expression
func ILike ¶
func ILike(left, pattern any) drops.Expression
func IfNull ¶
func IfNull(e, fallback any) drops.Expression
func InSub ¶ added in v0.5.0
func InSub(value any, sub drops.Expression) drops.Expression
InSub renders "<value> IN (<subquery>)".
func IsNotNull ¶
func IsNotNull(e any) drops.Expression
func IsNull ¶
func IsNull(e any) drops.Expression
func Lag ¶ added in v0.5.0
func Lag(expr any, args ...any) drops.Expression
Lag renders lagInFrame(expr [, offset [, default]]) — ClickHouse's spelling of the lag window function.
func LastValue ¶ added in v0.5.0
func LastValue(expr any) drops.Expression
func Lead ¶ added in v0.5.0
func Lead(expr any, args ...any) drops.Expression
Lead renders leadInFrame(expr [, offset [, default]]).
func Length ¶
func Length(e any) drops.Expression
func Like ¶
func Like(left, pattern any) drops.Expression
func LoggerHook ¶
func LoggerHook(log LoggerFunc, opts ...LoggerOptions) drops.Hook
LoggerHook re-exports drops.LoggerHook. See its documentation for behaviour and tuning options.
func Lower ¶
func Lower(e drops.Expression) drops.Expression
func Lt ¶
func Lt(left, right any) drops.Expression
func Lte ¶
func Lte(left, right any) drops.Expression
func Max ¶
func Max(e drops.Expression) drops.Expression
func Min ¶
func Min(e drops.Expression) drops.Expression
func Ne ¶
func Ne(left, right any) drops.Expression
func Not ¶
func Not(p drops.Expression) drops.Expression
func NotExists ¶ added in v0.5.0
func NotExists(q drops.Expression) drops.Expression
NotExists renders NOT EXISTS (<subquery>).
func NotInSub ¶ added in v0.5.0
func NotInSub(value any, sub drops.Expression) drops.Expression
NotInSub renders "<value> NOT IN (<subquery>)".
func Now ¶
func Now() drops.Expression
func NthValue ¶ added in v0.5.0
func NthValue(expr, n any) drops.Expression
func OptimizeTable ¶
func OptimizeTable(t *Table, final bool) drops.Expression
OptimizeTable triggers a merge round; final=true emits FINAL so all data is collapsed into a single part (expensive on large tables).
func Or ¶
func Or(preds ...drops.Expression) drops.Expression
func Over ¶ added in v0.5.0
func Over(fn drops.Expression, win *Window) drops.Expression
Over wraps an aggregate or window function with an OVER clause.
func Quantile ¶
func Quantile(level float64, e drops.Expression) drops.Expression
Quantile is the approximate quantile aggregate; level is in [0, 1].
clickhouse.Quantile(0.95, Latency) // 95th percentile
func QuantileExact ¶
func QuantileExact(level float64, e drops.Expression) drops.Expression
QuantileExact uses the exact algorithm; QuantileTiming is optimised for non-negative integers (latency in ms).
func QuantileTiming ¶
func QuantileTiming(level float64, e drops.Expression) drops.Expression
func Rank ¶ added in v0.5.0
func Rank() drops.Expression
func Round ¶
func Round(e any) drops.Expression
func RowNumber ¶ added in v0.5.0
func RowNumber() drops.Expression
func Subquery ¶ added in v0.5.0
func Subquery(q drops.Expression) drops.Expression
Subquery wraps an expression (typically a SELECT) in parentheses for use as a scalar subquery.
func Sum ¶
func Sum(e drops.Expression) drops.Expression
func ToDate ¶
func ToDate(e any) drops.Expression
func ToDateTime ¶
func ToDateTime(e any) drops.Expression
func ToSQL ¶
func ToSQL(e drops.Expression) (sql string, args []any)
ToSQL renders an Expression with the ClickHouse placeholder style. Use it when you need to inspect generated SQL outside of the builders (logging, snapshotting, tests).
func ToStartOfDay ¶
func ToStartOfDay(e any) drops.Expression
func ToStartOfHour ¶
func ToStartOfHour(e any) drops.Expression
func ToStartOfMinute ¶
func ToStartOfMinute(e any) drops.Expression
func ToStartOfMonth ¶
func ToStartOfMonth(e any) drops.Expression
func ToYYYYMM ¶
func ToYYYYMM(e any) drops.Expression
func ToYYYYMMDD ¶
func ToYYYYMMDD(e any) drops.Expression
func TruncateTable ¶
func TruncateTable(t *Table) drops.Expression
func TruncateTableIfExists ¶
func TruncateTableIfExists(t *Table) drops.Expression
func Uniq ¶
func Uniq(e drops.Expression) drops.Expression
Uniq is an approximate-distinct count (HyperLogLog).
func UniqExact ¶
func UniqExact(e drops.Expression) drops.Expression
UniqExact is the exact distinct count.
func UniqHLL12 ¶
func UniqHLL12(e drops.Expression) drops.Expression
UniqHLL12 is the configurable HLL approximation.
func Upper ¶
func Upper(e drops.Expression) drops.Expression
Types ¶
type AggregateSnapshot ¶ added in v0.5.0
type AggregateSnapshot struct {
AggregateType string
AggregateID string
Version int64
State json.RawMessage
CreatedAt time.Time
}
AggregateSnapshot persists the materialised state of an aggregate at a specific version so reads avoid replaying every event since the beginning of time. Optional — small aggregates can replay fast enough without.
type AuditCols ¶ added in v0.2.0
AuditCols holds the typed handles created by Audit. The type parameter mirrors the type of the supplied identity column.
type AuditMixin ¶ added in v0.2.0
AuditMixin registers "createdBy" and "updatedBy" columns of the same SQL type as Target. ClickHouse has no foreign-key enforcement, so the columns are plain scalars.
func (*AuditMixin[T]) Apply ¶ added in v0.2.0
func (m *AuditMixin[T]) Apply(t *Table)
Apply implements Mixin.
type CTE ¶ added in v0.5.0
type CTE struct {
// contains filtered or unexported fields
}
CTE describes one common table expression.
func CTEDef ¶ added in v0.5.0
func CTEDef(name string, query drops.Expression) *CTE
CTEDef returns a CTE definition binding name to query.
func (*CTE) Ref ¶ added in v0.5.0
func (c *CTE) Ref() drops.Expression
Ref references the CTE as a relation in a subsequent FROM/JOIN.
type CaseExpr ¶ added in v0.5.0
type CaseExpr struct {
// contains filtered or unexported fields
}
CaseExpr is the in-progress CASE expression.
func Case ¶ added in v0.5.0
func Case() *CaseExpr
Case begins a searched CASE expression. Chain When / Else / End. ClickHouse supports the standard CASE form (and multiIf as a function-style alternative).
func (*CaseExpr) End ¶ added in v0.5.0
func (c *CaseExpr) End() drops.Expression
End finalises the CASE expression.
type Col ¶
Col is the typed handle for a column whose Go value type is T.
func Add ¶
Add registers c with t and returns it. Type inference keeps the *Col[T] handle typed.
var Events = clickhouse.NewTable("events")
var (
EventID = clickhouse.Add(Events, clickhouse.UUID("id"))
EventTS = clickhouse.Add(Events, clickhouse.DateTime("ts", "UTC"))
)
Example ¶
ExampleAdd shows the canonical table+columns declaration pattern.
package main
import (
"fmt"
"github.com/bernardoforcillo/drops/clickhouse"
)
func main() {
logs := clickhouse.NewTable("logs")
id := clickhouse.Add(logs, clickhouse.UInt64("id"))
msg := clickhouse.Add(logs, clickhouse.String("message").LowCardinality())
fmt.Printf("%s, %s\n", id.Name(), msg.Name())
}
Output: id, message
func Custom ¶
Custom creates a column with an arbitrary type literal — useful for IPv4/IPv6, AggregateFunction(...), or vendor types not covered here.
func DateTime64 ¶
DateTime64 renders DateTime64(precision[, 'TZ']). precision is the sub-second precision (0..9).
func Decimal ¶
Decimal(precision, scale) — represented as string to preserve the full arbitrary-precision value.
func FixedString ¶
FixedString returns a FixedString(n) column. n must be > 0.
func (*Col[T]) Between ¶
func (c *Col[T]) Between(lo, hi T) drops.Expression
func (*Col[T]) Codec ¶
Codec sets the CODEC(...) clause — e.g. Codec("ZSTD(3)"), Codec("Delta, LZ4"). Pass the inner text without parentheses.
func (*Col[T]) Comment ¶
Comment attaches a free-form comment that ends up in the CREATE TABLE column definition.
func (*Col[T]) Default ¶
Default sets a raw SQL default expression (e.g. "0", "now()", "'pending'", "uuidv4()"). DEFAULT expressions cannot be parameterised.
func (*Col[T]) Eq ¶
func (c *Col[T]) Eq(v T) drops.Expression
func (*Col[T]) Expr ¶
func (c *Col[T]) Expr(e drops.Expression) ColumnValue
Expr binds an arbitrary SQL expression to the column in an INSERT.
func (*Col[T]) Gt ¶
func (c *Col[T]) Gt(v T) drops.Expression
func (*Col[T]) Gte ¶
func (c *Col[T]) Gte(v T) drops.Expression
func (*Col[T]) In ¶
func (c *Col[T]) In(values ...T) drops.Expression
func (*Col[T]) IsNotNull ¶
func (c *Col[T]) IsNotNull() drops.Expression
func (*Col[T]) IsNull ¶
func (c *Col[T]) IsNull() drops.Expression
func (*Col[T]) LowCardinality ¶
LowCardinality wraps the column type in LowCardinality(...).
func (*Col[T]) Lt ¶
func (c *Col[T]) Lt(v T) drops.Expression
func (*Col[T]) Lte ¶
func (c *Col[T]) Lte(v T) drops.Expression
func (*Col[T]) Ne ¶
func (c *Col[T]) Ne(v T) drops.Expression
func (*Col[T]) NotIn ¶
func (c *Col[T]) NotIn(values ...T) drops.Expression
func (*Col[T]) Val ¶
func (c *Col[T]) Val(v T) ColumnValue
Val binds a typed value as the column's payload in an INSERT row.
type ColRef ¶
type ColRef interface {
drops.Expression
// contains filtered or unexported methods
}
ColRef is implemented by *Column and *Col[T]. Use it where the value type doesn't matter (engine ORDER BY / PARTITION BY, index columns, SELECT projections).
type Column ¶
type Column struct {
// contains filtered or unexported fields
}
Column is the type-erased AST node for a column reference. Most user code holds a *Col[T] (returned by every type constructor) which embeds *Column and adds type-safe builder + operator methods.
func (*Column) Asc ¶
func (c *Column) Asc() drops.Expression
func (*Column) DefaultSQL ¶
DefaultSQL returns the raw DEFAULT expression.
func (*Column) Desc ¶
func (c *Column) Desc() drops.Expression
func (*Column) HasDefault ¶
HasDefault reports whether a DEFAULT clause was set.
func (*Column) IsNullable ¶
IsNullable reports whether the column was wrapped in Nullable().
type ColumnType ¶
type ColumnType interface {
TypeSQL() string
}
ColumnType describes the SQL type literal of a ClickHouse column — the bit that goes right after the column name in DDL.
func TypeArray ¶
func TypeArray(inner ColumnType) ColumnType
TypeArray wraps an inner type as Array(T).
func TypeEnum8 ¶
func TypeEnum8(values map[string]int8) ColumnType
TypeEnum8 / TypeEnum16 render the labelled enum types.
TypeEnum8(map[string]int8{"a": 1, "b": 2})
The output order is sorted by value so the generated DDL is stable across runs.
func TypeEnum16 ¶
func TypeEnum16(values map[string]int16) ColumnType
func TypeLowCardinality ¶
func TypeLowCardinality(inner ColumnType) ColumnType
TypeLowCardinality wraps an inner type as LowCardinality(T).
func TypeNullable ¶
func TypeNullable(inner ColumnType) ColumnType
TypeNullable wraps an inner type as Nullable(T).
func TypeTuple ¶
func TypeTuple(members ...ColumnType) ColumnType
TypeTuple renders Tuple(T1, T2, ...).
type ColumnValue ¶
type ColumnValue interface {
// contains filtered or unexported methods
}
ColumnValue pairs a target column with the value or expression to bind for it in an INSERT row. Construct one with (*Col[T]).Val or (*Col[T]).Expr.
type Cursor ¶ added in v0.5.0
type Cursor string
Cursor is an opaque page marker — obtain one from EncodeCursor and pass it to SelectBuilder.AfterCursor / BeforeCursor.
func EncodeCursor ¶ added in v0.5.0
func EncodeCursor(spec CursorSpec, values ...any) (Cursor, error)
EncodeCursor builds a cursor from values matching the spec, one per key in declaration order. Returns an error when values is the wrong length or contains an unsupported type.
type CursorSpec ¶ added in v0.5.0
type CursorSpec struct {
Keys []OrderKey
}
CursorSpec is the ordered list of keys that defines the cursor shape — used by OrderByCursor and AfterCursor so the same shape drives both the ORDER BY and the keyset WHERE.
func NewCursorSpec ¶ added in v0.5.0
func NewCursorSpec(keys ...OrderKey) CursorSpec
NewCursorSpec returns a CursorSpec carrying the supplied keys in declaration order. The last key should be a primary key (or otherwise unique column) so equal leading values still resolve to a deterministic page boundary.
type DB ¶
type DB struct {
// contains filtered or unexported fields
}
DB is the entry point for issuing ClickHouse queries through a drops.Driver. The same Hook / Ping / Close / InTx contract as drops/pg's DB, but every emitted statement uses "?" placeholders.
Safe for concurrent use by multiple goroutines provided the underlying Driver is. Builders returned by Select / Insert are not — create one per query.
func (*DB) Begin ¶
Begin opens a transaction. Note that ClickHouse only supports transactions in the MergeTree family from recent versions and the guarantees are weaker than PostgreSQL — use sparingly.
func (*DB) InTx ¶
InTx runs fn inside a transaction. Rollback uses a detached context with a short timeout so a cancelled caller-ctx doesn't poison cleanup. Hook events fire for begin/commit/rollback.
func (*DB) Select ¶
func (db *DB) Select(cols ...drops.Expression) *SelectBuilder
Select begins a SELECT.
Example ¶
ExampleDB_Select shows a quantile + grouped analytics query.
package main
import (
"fmt"
"github.com/bernardoforcillo/drops/clickhouse"
)
// Schema fixtures used by the godoc examples — picked from a small
// analytics-style table so the rendered SQL is recognisable.
var (
exEvents = clickhouse.NewTable("events")
exEventTS = clickhouse.Add(exEvents, clickhouse.DateTime("ts", "UTC"))
exEventDur = clickhouse.Add(exEvents, clickhouse.Float64("duration_ms"))
)
func main() {
db := clickhouse.New(nil)
sql, _ := db.Select(
clickhouse.As(clickhouse.ToStartOfDay(exEventTS), "day"),
clickhouse.As(clickhouse.QuantileTiming(0.95, exEventDur), "p95"),
clickhouse.As(clickhouse.CountAll(), "hits"),
).
From(exEvents).
GroupBy(clickhouse.ToStartOfDay(exEventTS)).
OrderBy(exEventTS.Asc()).
ToSQL()
fmt.Println(sql)
}
Output: SELECT toStartOfDay("events"."ts") AS "day", quantileTiming(?)("events"."duration_ms") AS "p95", count() AS "hits" FROM "events" GROUP BY toStartOfDay("events"."ts") ORDER BY "events"."ts" ASC
func (*DB) WithHook ¶
WithHook returns a shallow copy with hook installed; nil removes.
Example ¶
ExampleDB_WithHook shows attaching the dialect-neutral LoggerHook to a ClickHouse DB.
package main
import (
"context"
"fmt"
"github.com/bernardoforcillo/drops"
"github.com/bernardoforcillo/drops/clickhouse"
)
// Schema fixtures used by the godoc examples — picked from a small
// analytics-style table so the rendered SQL is recognisable.
var (
exEvents = clickhouse.NewTable("events")
exEventTS = clickhouse.Add(exEvents, clickhouse.DateTime("ts", "UTC"))
exEventUID = clickhouse.Add(exEvents, clickhouse.UInt64("userId"))
)
func init() {
exEvents.
Engine(clickhouse.MergeTree()).
OrderBy(exEventTS, exEventUID).
PartitionBy(clickhouse.ToYYYYMM(exEventTS))
}
func main() {
db := clickhouse.New(exampleNoopDriver{}).WithHook(
drops.LoggerHook(func(format string, args ...any) {
fmt.Printf("logged: "+format+"\n", args...)
}),
)
_, _ = db.Exec(context.Background(), "SELECT 1")
// Sample output omitted — duration is non-deterministic. The hook
// produces one line per call with kind, status, elapsed and SQL.
}
// exampleNoopDriver lets the godoc examples render through DB without
// pulling in a real ClickHouse driver. Production code never embeds
// a no-op.
type exampleNoopDriver struct{}
func (exampleNoopDriver) Exec(context.Context, string, ...any) (drops.Result, error) {
return exampleNoopResult{}, nil
}
func (exampleNoopDriver) Query(context.Context, string, ...any) (drops.Rows, error) {
return nil, fmt.Errorf("not implemented")
}
func (exampleNoopDriver) Begin(context.Context) (drops.Tx, error) {
return nil, fmt.Errorf("not implemented")
}
type exampleNoopResult struct{}
func (exampleNoopResult) RowsAffected() (int64, error) { return 0, nil }
Output:
type Engine ¶
Engine is the table-engine specification. ClickHouse requires every table to declare one; Engine values render as the text right after the ENGINE = keyword inside CREATE TABLE.
Common engines are provided as constructor helpers below; for anything else (Distributed, Kafka, MaterializedView source engines, AggregatingMergeTree with explicit parameters) use Raw.
func AggregatingMergeTree ¶
func AggregatingMergeTree() Engine
AggregatingMergeTree is the empty-constructor form.
func CollapsingMergeTree ¶
CollapsingMergeTree(sign_column).
func MergeTree ¶
func MergeTree() Engine
MergeTree returns the MergeTree() engine. ORDER BY / PARTITION BY / settings are configured on the table itself (Table.OrderBy etc.).
func Raw ¶
Raw builds an engine spec from a literal string. Use for engines the typed constructors don't cover.
clickhouse.Raw("Distributed(cluster, db, table, rand())")
func ReplacingMergeTree ¶
ReplacingMergeTree(version_column) — version is optional; pass an empty string for the default form ReplacingMergeTree().
func ReplicatedMergeTree ¶
ReplicatedMergeTree(zk_path, replica). The path/replica strings are passed verbatim — typically use macros: '/clickhouse/tables/{shard}/foo'.
func SummingMergeTree ¶
SummingMergeTree(columns...) — optional list of columns to sum.
func VersionedCollapsingMergeTree ¶
VersionedCollapsingMergeTree(sign_column, version_column).
type Entity ¶ added in v0.2.0
type Entity[T any] struct { // contains filtered or unexported fields }
Entity binds a Go struct T to a Table and precomputes the column ↔ field mapping used by the CRUD shortcuts. ClickHouse exposes only Insert and Select builders (mutations are async ALTERs and not first-class), so the Entity surface is intentionally narrow:
- Create / CreateMany insert one or many rows from the typed struct. There is no RETURNING in ClickHouse, so the caller is responsible for filling every column they care about.
- Query returns a typed builder for SELECT chains that scan into T directly.
Declare an Entity once at package level:
type Event struct {
ID string `drop:"id"`
UserID uint64 `drop:"userId"`
Kind string `drop:"kind"`
Ts time.Time `drop:"ts"`
}
var (
Events = clickhouse.NewTable("events").Engine(clickhouse.MergeTree())
EventID = clickhouse.Add(Events, clickhouse.UUID("id"))
EventEnt = clickhouse.NewEntity[Event](Events)
)
func NewEntity ¶ added in v0.2.0
NewEntity validates that T is a struct and builds the column ↔ field index map. It panics on a non-struct type because schema declarations are typically loaded at process startup.
Field matching mirrors the row scanner: `drop:"colname"` tag wins, otherwise field name and snake_case form are tried.
func (*Entity[T]) Create ¶ added in v0.2.0
Create inserts a single row from r. ClickHouse has no RETURNING, so r is not refreshed — any DEFAULT-driven values (timestamps, UUIDs) stay zero on the Go side.
func (*Entity[T]) CreateMany ¶ added in v0.2.0
CreateMany inserts a batch of rows. This is the typical analytics pattern; for very large batches drop down to the native columnar protocol via clickhouse-go's Prepare/Exec loop. Validators run against every row before any SQL is issued — the first failure aborts the whole batch.
func (*Entity[T]) Query ¶ added in v0.2.0
func (e *Entity[T]) Query(db *DB) *EntityQuery[T]
Query returns a typed query builder that scans into T.
type EntityQuery ¶ added in v0.2.0
type EntityQuery[T any] struct { // contains filtered or unexported fields }
EntityQuery is the typed counterpart of SelectBuilder — same shape, but its executors return ([]T, error) and (T, error) directly.
func (*EntityQuery[T]) All ¶ added in v0.2.0
func (q *EntityQuery[T]) All(ctx context.Context) ([]T, error)
All executes the query and returns the matching rows as a typed slice.
func (*EntityQuery[T]) Limit ¶ added in v0.2.0
func (q *EntityQuery[T]) Limit(n int64) *EntityQuery[T]
Limit sets the LIMIT.
func (*EntityQuery[T]) Offset ¶ added in v0.2.0
func (q *EntityQuery[T]) Offset(n int64) *EntityQuery[T]
Offset sets the OFFSET.
func (*EntityQuery[T]) One ¶ added in v0.2.0
func (q *EntityQuery[T]) One(ctx context.Context) (T, error)
One executes the query and returns the first matching row. Returns ErrNoRows if the query produces no rows.
func (*EntityQuery[T]) OrderBy ¶ added in v0.2.0
func (q *EntityQuery[T]) OrderBy(exprs ...drops.Expression) *EntityQuery[T]
OrderBy appends ORDER BY expressions.
func (*EntityQuery[T]) Prewhere ¶ added in v0.2.0
func (q *EntityQuery[T]) Prewhere(preds ...drops.Expression) *EntityQuery[T]
Prewhere appends PREWHERE predicates.
func (*EntityQuery[T]) Unscoped ¶ added in v0.2.0
func (q *EntityQuery[T]) Unscoped() *EntityQuery[T]
Unscoped opts out of the table's DefaultFilter predicates.
func (*EntityQuery[T]) Where ¶ added in v0.2.0
func (q *EntityQuery[T]) Where(preds ...drops.Expression) *EntityQuery[T]
Where appends predicates joined by AND.
type EnvelopeCipher ¶ added in v0.5.0
type EnvelopeCipher struct {
// contains filtered or unexported fields
}
EnvelopeCipher performs AES-256-GCM with per-call data encryption keys wrapped by the configured KMS. Ciphertext format on the wire:
[magic: 1 byte] [wrappedDEK length: uint32 BE] [wrappedDEK] [nonce: 12 bytes] [ciphertext + GCM tag]
The header is fixed-length-prefixed so the decryptor can split without scanning. The magic byte versions the format.
func NewEnvelopeCipher ¶ added in v0.5.0
func NewEnvelopeCipher(kms KMS) *EnvelopeCipher
NewEnvelopeCipher wraps kms in the cipher contract. Panics on a nil KMS — encryption with a nil KMS is never a useful programmer intent.
func (*EnvelopeCipher) Decrypt ¶ added in v0.5.0
Decrypt undoes Encrypt — unwraps the DEK via KMS, then opens the ciphertext with AES-GCM. Returns an error when the input has the wrong magic byte, truncated header, or fails the AEAD check.
type Event ¶ added in v0.5.0
type Event struct {
// Offset is a nanosecond-epoch timestamp that acts as the
// store-wide ordering key. Use it as the high-watermark when
// streaming for projections.
Offset int64
// AggregateType / AggregateID identify the stream.
AggregateType string
AggregateID string
// Version is the per-stream offset starting at 0. Append's
// expectedVersion compares against this.
Version int64
// Type is the application-level event name (e.g. "matchStarted").
Type string
// Payload is the JSON-encoded event body.
Payload json.RawMessage
// Headers carry tracing / correlation metadata.
Headers map[string]string
// CreatedAt is the wall-clock time of the append.
CreatedAt time.Time
}
Event is one entry in the store.
type EventInput ¶ added in v0.5.0
type EventInput struct {
// Type is required — names the event.
Type string
// Payload is encoded with encoding/json. Pre-encoded
// json.RawMessage / []byte / string pass through untouched.
Payload any
// Headers attach tracing / correlation metadata stored as JSON.
Headers map[string]string
}
EventInput is the per-event payload passed to Append.
type EventStore ¶ added in v0.5.0
type EventStore struct {
// contains filtered or unexported fields
}
EventStore is the append-only event log bound to a single table.
func NewEventStore ¶ added in v0.5.0
func NewEventStore(db *DB, table string) *EventStore
NewEventStore returns a store bound to db. The table name is the SQL identifier used for all queries; use NewEventStoreTable to declare matching DDL.
func (*EventStore) Append ¶ added in v0.5.0
func (s *EventStore) Append(ctx context.Context, aggregateType, aggregateID string, expectedVersion int64, events ...EventInput) error
Append writes events to the stream identified by (aggregateType, aggregateID) starting at expectedVersion+1. Returns ErrConcurrencyConflict if the stream's head has advanced past expectedVersion.
The check is non-atomic in ClickHouse (no unique-constraint enforcement on MergeTree). For strict single-writer semantics, serialize appends to the same aggregate outside the database.
func (*EventStore) LatestVersion ¶ added in v0.5.0
func (s *EventStore) LatestVersion(ctx context.Context, aggregateType, aggregateID string) (int64, error)
LatestVersion returns the highest version recorded for the stream, or -1 when the stream is empty. Use this before Append to compute the expectedVersion for a fresh write.
func (*EventStore) Load ¶ added in v0.5.0
func (s *EventStore) Load(ctx context.Context, aggregateType, aggregateID string, fromVersion int64) ([]Event, error)
Load returns events for an aggregate in version order, starting after fromVersion. Pass 0 to read from the beginning.
func (*EventStore) LoadSnapshot ¶ added in v0.5.0
func (s *EventStore) LoadSnapshot(ctx context.Context, table, aggregateType, aggregateID string) (AggregateSnapshot, bool, error)
LoadSnapshot fetches the latest snapshot for an aggregate. The query uses FINAL so it reflects the deduplicated (latest-version) row even before CH background merges have run. Returns ok=false when no snapshot exists — fall back to replaying from version 0.
func (*EventStore) SaveSnapshot ¶ added in v0.5.0
func (s *EventStore) SaveSnapshot(ctx context.Context, table string, snap AggregateSnapshot) error
SaveSnapshot inserts a new snapshot. With ReplacingMergeTree(version) as the table engine, ClickHouse will eventually deduplicate to the highest-version row per aggregate. Use LoadSnapshot (which reads FINAL) to always get the latest.
func (*EventStore) Stream ¶ added in v0.5.0
Stream returns events from the global log starting after fromOffset, up to limit rows. fromOffset is a nanosecond-epoch timestamp — use Event.Offset from the last row of the previous batch. Pass 0 to read from the beginning. Used by projection workers to fan events out into derived tables.
type Factory ¶ added in v0.5.0
type Factory[T any] struct { // contains filtered or unexported fields }
Factory builds and inserts test rows for an Entity. The template callback returns a fresh value each call; the supplied seq is a monotonically increasing counter the template can interpolate into fields that need to stay unique across a batch.
var EventFactory = clickhouse.NewFactory(EventEnt, func(seq int) Event {
return Event{
ID: fmt.Sprintf("evt-%d", seq),
UserID: uint64(seq),
Kind: "pageview",
Ts: time.Now(),
}
})
// In a test
e, _ := EventFactory.Create(ctx, db) // one row, inserted
es, _ := EventFactory.CreateN(ctx, db, 100) // batch insert
pv := EventFactory.With(func(e *Event) { e.Kind = "purchase" })
p, _ := pv.Create(ctx, db)
Factories are safe to share across goroutines — the sequence counter is atomic. Call Reset between subtests if you need stable identifiers.
func NewFactory ¶ added in v0.5.0
NewFactory binds template to entity. The template fires once per Build / Create call; the seq it receives is the post-increment counter so the first invocation sees seq=1.
func (*Factory[T]) Build ¶ added in v0.5.0
func (f *Factory[T]) Build() T
Build returns a fresh value without touching the database.
func (*Factory[T]) BuildN ¶ added in v0.5.0
BuildN returns n freshly templated values without touching the database. Each value receives a distinct seq.
func (*Factory[T]) Create ¶ added in v0.5.0
Create builds one value and inserts it. ClickHouse has no RETURNING so the returned T is the value as built — server-side defaults (e.g. DEFAULT expressions) are not reflected back.
func (*Factory[T]) CreateN ¶ added in v0.5.0
CreateN builds n values and inserts them in a single batch statement via Entity.CreateMany. Returns the templated values; server-side defaults are not read back (matches Entity.CreateMany's semantics).
func (*Factory[T]) Reset ¶ added in v0.5.0
func (f *Factory[T]) Reset()
Reset rewinds the sequence counter to zero. Typically called in test setup between subtests so identifiers stay stable from one run to the next.
func (*Factory[T]) Sequence ¶ added in v0.5.0
Sequence returns the current sequence value without advancing. Useful for assertions on factory state in test helpers.
func (*Factory[T]) With ¶ added in v0.5.0
With returns a child factory whose Build / Create calls run the parent template, then apply mutate to the result. Use it to spin off variant factories — different event kinds, user tiers, etc. — without redeclaring the whole template.
Child factories share the parent's sequence counter so identifiers remain unique across both.
type InsertBuilder ¶
type InsertBuilder struct {
// contains filtered or unexported fields
}
InsertBuilder composes an INSERT INTO …(cols) VALUES (…), (…), … statement. ClickHouse-optimal bulk loads use the native columnar protocol via clickhouse-go's Prepare/Exec loop; for that path drop down to the driver directly. This builder is the convenient form for small batches and one-off rows.
func (*InsertBuilder) Columns ¶
func (i *InsertBuilder) Columns(cols ...ColRef) *InsertBuilder
Columns explicitly fixes the column list (and order) before any Row call. Useful when the first row in your batch omits columns you want present in the SQL.
func (*InsertBuilder) Row ¶
func (i *InsertBuilder) Row(values ...ColumnValue) *InsertBuilder
Row appends a single row. The first Row fixes the column list.
func (*InsertBuilder) Rows ¶
func (i *InsertBuilder) Rows(rows ...[]ColumnValue) *InsertBuilder
Rows appends multiple rows in one call.
func (*InsertBuilder) ToSQL ¶
func (i *InsertBuilder) ToSQL() (sql string, args []any)
ToSQL renders the statement.
func (*InsertBuilder) WriteSQL ¶
func (i *InsertBuilder) WriteSQL(b *drops.Builder)
WriteSQL renders the INSERT statement.
type InsertHook ¶ added in v0.2.0
type InsertHook interface {
BeforeInsert(ctx *InsertHookCtx)
}
InsertHook is invoked once per INSERT statement, before rendering.
type InsertHookCtx ¶ added in v0.2.0
type InsertHookCtx struct {
// contains filtered or unexported fields
}
InsertHookCtx exposes which columns the caller already bound and lets the hook append additional bindings that apply to every row.
func (*InsertHookCtx) Has ¶ added in v0.2.0
func (c *InsertHookCtx) Has(col *Column) bool
Has reports whether col is already bound on the INSERT.
func (*InsertHookCtx) Set ¶ added in v0.2.0
func (c *InsertHookCtx) Set(v ColumnValue)
Set binds a typed ColumnValue (e.g. the result of (*Col[T]).Val(v)).
func (*InsertHookCtx) SetExpr ¶ added in v0.2.0
func (c *InsertHookCtx) SetExpr(col *Column, expr drops.Expression)
SetExpr binds expr to col across every row, unless col is already bound — typical for DB-evaluated defaults such as drops.Raw("now()").
type InsertHookFunc ¶ added in v0.2.0
type InsertHookFunc func(*InsertHookCtx)
InsertHookFunc adapts a plain function to the InsertHook interface.
func (InsertHookFunc) BeforeInsert ¶ added in v0.2.0
func (f InsertHookFunc) BeforeInsert(ctx *InsertHookCtx)
BeforeInsert implements InsertHook.
type KMS ¶ added in v0.5.0
type KMS interface {
// Wrap encrypts dek under the master key. drops calls this
// once per encrypted column write.
Wrap(ctx context.Context, dek []byte) ([]byte, error)
// Unwrap decrypts wrappedDEK. drops calls this once per
// encrypted column read.
Unwrap(ctx context.Context, wrappedDEK []byte) ([]byte, error)
}
KMS is the key-management contract drops uses for envelope encryption. It wraps and unwraps a data encryption key (DEK) without exposing the master key to drops.
Real implementations live in user code so drops doesn't carry a dependency on any specific KMS SDK. Common adapters are around 30 lines each.
type LocalKMS ¶ added in v0.5.0
type LocalKMS struct {
// contains filtered or unexported fields
}
LocalKMS is an in-process KMS backed by a 32-byte master key. Useful for tests and local development — DO NOT use in production where the master key needs to come from an HSM / KMS service.
func NewLocalKMS ¶ added in v0.5.0
NewLocalKMS wraps key (must be 32 bytes for AES-256-GCM) as a LocalKMS. The same key must be supplied on every process restart — losing it loses every encrypted row.
type LoggerFunc ¶
type LoggerFunc = drops.LoggerFunc
LoggerFunc / LoggerOptions / LoggerHook re-export the dialect-neutral helpers from the root drops package. A single Hook function works against pg.DB, clickhouse.DB and qdrant.Client unchanged.
type LoggerOptions ¶
type LoggerOptions = drops.LoggerOptions
LoggerFunc / LoggerOptions / LoggerHook re-export the dialect-neutral helpers from the root drops package. A single Hook function works against pg.DB, clickhouse.DB and qdrant.Client unchanged.
type MatView ¶ added in v0.5.0
type MatView struct {
// Name is the (unquoted) view name.
Name string
// DependsOn lists the upstream relations (base tables and other
// views) feeding into this view. Used to pick refresh order in
// RefreshDownstream.
DependsOn []string
// Every, when non-zero, schedules a periodic refresh under Start.
Every time.Duration
}
MatView registers one refreshable materialized view with the manager.
type MatViewManager ¶ added in v0.5.0
type MatViewManager struct {
// contains filtered or unexported fields
}
MatViewManager tracks registered views and coordinates refresh across dependents.
func NewMatViewManager ¶ added in v0.5.0
func NewMatViewManager(db *DB) *MatViewManager
NewMatViewManager returns an empty manager bound to db.
func (*MatViewManager) Add ¶ added in v0.5.0
func (m *MatViewManager) Add(v MatView) *MatViewManager
Add registers v. Returns the manager for chaining. Duplicate names overwrite the prior entry.
func (*MatViewManager) LastRefresh ¶ added in v0.5.0
func (m *MatViewManager) LastRefresh(name string) time.Time
LastRefresh reports the last successful refresh time for a view, or zero if it has never been refreshed by this manager.
func (*MatViewManager) Refresh ¶ added in v0.5.0
func (m *MatViewManager) Refresh(ctx context.Context, name string) error
Refresh issues SYSTEM REFRESH VIEW on the single named view. Returns an error when the view is not registered or the SQL fails.
func (*MatViewManager) RefreshAll ¶ added in v0.5.0
func (m *MatViewManager) RefreshAll(ctx context.Context) error
RefreshAll refreshes every registered view in topological order. Failures abort the run.
func (*MatViewManager) RefreshDownstream ¶ added in v0.5.0
func (m *MatViewManager) RefreshDownstream(ctx context.Context, upstream string) error
RefreshDownstream refreshes every view registered with the manager that transitively depends on upstream, in topological order. Used when an upstream base table has been written to and callers want to fan the refresh out to derived views.
func (*MatViewManager) Start ¶ added in v0.5.0
func (m *MatViewManager) Start(ctx context.Context) error
Start runs the periodic scheduler honouring each view's Every interval. Blocks until ctx is cancelled.
func (*MatViewManager) Views ¶ added in v0.5.0
func (m *MatViewManager) Views() []MatView
Views returns the registered views in name-sorted order.
func (*MatViewManager) WithPollInterval ¶ added in v0.5.0
func (m *MatViewManager) WithPollInterval(d time.Duration) *MatViewManager
WithPollInterval overrides how often Start wakes to check for due refreshes. The default (250ms) is fine for typical schedules measured in seconds; increase it for minute-scale schedules to spare the timer.
type Mixin ¶ added in v0.2.0
type Mixin interface {
Apply(*Table)
}
Mixin is the richer companion of the plain template functions in template.go. ClickHouse exposes only InsertHook and SelectBuilder default filters — UPDATE/DELETE happen via async ALTERs and are not modelled by builders — so the rich mixins here are more limited than in drops/pg.
clickhouse.ApplyMixins(Events,
clickhouse.UUIDPrimaryKeyMixin{},
clickhouse.TimestampsMixin{}, // adds createdAt, updatedAt
clickhouse.SoftDeleteMixin{}, // adds deletedAt + default scope
)
type MixinFunc ¶ added in v0.2.0
type MixinFunc func(*Table)
MixinFunc adapts a plain function to the Mixin interface.
type NullsOrdering ¶ added in v0.5.0
type NullsOrdering string
NullsOrdering is the NULLS FIRST / NULLS LAST modifier on an ORDER BY clause.
const ( // NullsDefault inherits ClickHouse's per-direction default. NullsDefault NullsOrdering = "" // NullsFirst pushes NULL values to the start of the order. NullsFirst NullsOrdering = "FIRST" // NullsLast pushes NULL values to the end of the order. NullsLast NullsOrdering = "LAST" )
type OrderKey ¶ added in v0.5.0
type OrderKey struct {
// Col is the column referenced by both the ORDER BY clause
// and the keyset WHERE comparison.
Col ColRef
// Desc selects descending order. Defaults to ascending.
Desc bool
// Nulls picks the NULLS FIRST / NULLS LAST clause.
Nulls NullsOrdering
}
OrderKey is one (column, direction) pair making up a cursor shape. Combine multiple OrderKey values in a CursorSpec to form a stable ordering — the typical pattern is (sort_column DESC, primary_key DESC) so rows with identical sort values still produce a unique page boundary.
type PlanStep ¶ added in v0.5.0
type PlanStep struct {
ID string
Name string
Branch string
Description string
Action string // "apply" or "rollback"
}
PlanStep is one action in the dry-run output of Plan().
type SelectBuilder ¶
type SelectBuilder struct {
// contains filtered or unexported fields
}
SelectBuilder composes a ClickHouse SELECT. It mirrors the drops/pg surface (Where, OrderBy, GroupBy, Limit, joins) plus CH-specific clauses (PREWHERE, FINAL, SAMPLE, SETTINGS).
func (*SelectBuilder) AfterCursor ¶ added in v0.5.0
func (s *SelectBuilder) AfterCursor(spec CursorSpec, c Cursor) *SelectBuilder
AfterCursor appends the keyset WHERE clause for forward paging past cursor c. Decoding errors are stored on the builder and returned by Rows / All / One so callers don't have to guard every chained call.
func (*SelectBuilder) All ¶
func (s *SelectBuilder) All(ctx context.Context, dest any) error
All scans every row into dest.
func (*SelectBuilder) AllJoin ¶
func (s *SelectBuilder) AllJoin(t *Table, on drops.Expression) *SelectBuilder
func (*SelectBuilder) AnyJoin ¶
func (s *SelectBuilder) AnyJoin(t *Table, on drops.Expression) *SelectBuilder
func (*SelectBuilder) AsofJoin ¶
func (s *SelectBuilder) AsofJoin(t *Table, on drops.Expression) *SelectBuilder
func (*SelectBuilder) BeforeCursor ¶ added in v0.5.0
func (s *SelectBuilder) BeforeCursor(spec CursorSpec, c Cursor) *SelectBuilder
BeforeCursor is the reverse — appends WHERE constraints that page backward past c. Combine with a reversed ORDER BY (or reverse the returned slice on the caller side) to present the page in the expected order.
func (*SelectBuilder) Count ¶
func (s *SelectBuilder) Count(ctx context.Context) (int64, error)
Count wraps the current SELECT in `SELECT count() FROM (... )`.
func (*SelectBuilder) Distinct ¶
func (s *SelectBuilder) Distinct() *SelectBuilder
Distinct toggles SELECT DISTINCT.
func (*SelectBuilder) Final ¶
func (s *SelectBuilder) Final() *SelectBuilder
Final appends FINAL after the table, forcing CH to merge parts at read time (handy with ReplacingMergeTree / CollapsingMergeTree when you accept the cost).
func (*SelectBuilder) From ¶
func (s *SelectBuilder) From(t *Table) *SelectBuilder
From sets the FROM table. Required before execution.
func (*SelectBuilder) FullJoin ¶
func (s *SelectBuilder) FullJoin(t *Table, on drops.Expression) *SelectBuilder
func (*SelectBuilder) GroupBy ¶
func (s *SelectBuilder) GroupBy(exprs ...drops.Expression) *SelectBuilder
GroupBy / Having / OrderBy / Limit / Offset.
func (*SelectBuilder) Having ¶
func (s *SelectBuilder) Having(preds ...drops.Expression) *SelectBuilder
func (*SelectBuilder) Join ¶
func (s *SelectBuilder) Join(t *Table, on drops.Expression) *SelectBuilder
Join / LeftJoin / RightJoin / FullJoin / AnyJoin / AllJoin / AsofJoin.
func (*SelectBuilder) LeftJoin ¶
func (s *SelectBuilder) LeftJoin(t *Table, on drops.Expression) *SelectBuilder
func (*SelectBuilder) Limit ¶
func (s *SelectBuilder) Limit(n int64) *SelectBuilder
func (*SelectBuilder) Offset ¶
func (s *SelectBuilder) Offset(n int64) *SelectBuilder
func (*SelectBuilder) One ¶
func (s *SelectBuilder) One(ctx context.Context, dest any) error
One scans the first row into dest. Returns ErrNoRows if empty.
func (*SelectBuilder) OrderBy ¶
func (s *SelectBuilder) OrderBy(exprs ...drops.Expression) *SelectBuilder
func (*SelectBuilder) OrderByCursor ¶ added in v0.5.0
func (s *SelectBuilder) OrderByCursor(spec CursorSpec) *SelectBuilder
OrderByCursor sets ORDER BY according to spec. The same spec must be passed to AfterCursor / BeforeCursor on subsequent pages so the keyset WHERE matches.
func (*SelectBuilder) Prewhere ¶
func (s *SelectBuilder) Prewhere(preds ...drops.Expression) *SelectBuilder
Prewhere adds a PREWHERE predicate — evaluated before the main WHERE, with the right primary-key columns it can dramatically cut scanned data on MergeTree tables.
func (*SelectBuilder) RightJoin ¶
func (s *SelectBuilder) RightJoin(t *Table, on drops.Expression) *SelectBuilder
func (*SelectBuilder) SampleBy ¶
func (s *SelectBuilder) SampleBy(e any) *SelectBuilder
SampleBy adds a SAMPLE clause (e.g. SampleBy(0.1) for 10%).
func (*SelectBuilder) Setting ¶
func (s *SelectBuilder) Setting(key, value string) *SelectBuilder
Setting appends a "key = value" pair to the SETTINGS clause.
func (*SelectBuilder) ToSQL ¶
func (s *SelectBuilder) ToSQL() (sql string, args []any)
ToSQL renders the statement using the ClickHouse placeholder style.
func (*SelectBuilder) Unscoped ¶ added in v0.2.0
func (s *SelectBuilder) Unscoped() *SelectBuilder
Unscoped opts out of the FROM table's DefaultFilter predicates for this SELECT.
func (*SelectBuilder) Where ¶
func (s *SelectBuilder) Where(preds ...drops.Expression) *SelectBuilder
Where appends predicates joined by AND.
func (*SelectBuilder) With ¶ added in v0.5.0
func (s *SelectBuilder) With(ctes ...*CTE) *SelectBuilder
With prepends a WITH clause to the SELECT. Multiple calls accumulate.
func (*SelectBuilder) WriteSQL ¶
func (s *SelectBuilder) WriteSQL(b *drops.Builder)
WriteSQL renders the SELECT.
type SoftDeleteCols ¶ added in v0.2.0
SoftDeleteCols holds the typed handle created by SoftDelete.
func SoftDelete ¶ added in v0.2.0
func SoftDelete(t *Table) SoftDeleteCols
SoftDelete appends a Nullable(DateTime) "deletedAt" column to t. A row is treated as live while deletedAt IS NULL.
type SoftDeleteMixin ¶ added in v0.2.0
type SoftDeleteMixin struct {
Cols SoftDeleteCols
}
SoftDeleteMixin registers a Nullable(DateTime) "deletedAt" column and a default filter that excludes already-deleted rows from SELECTs. ClickHouse has no UPDATE/DELETE builder, so the "soft delete the row" operation is left to the caller as a raw ALTER TABLE … UPDATE statement.
func (*SoftDeleteMixin) Apply ¶ added in v0.2.0
func (m *SoftDeleteMixin) Apply(t *Table)
Apply implements Mixin.
type Table ¶
type Table struct {
// contains filtered or unexported fields
}
Table represents a ClickHouse table. Beyond columns, it carries the engine spec plus the optional clauses every CREATE TABLE may stipulate: ORDER BY, PARTITION BY, PRIMARY KEY, SAMPLE BY, TTL, and the SETTINGS bag.
func ApplyMixins ¶ added in v0.2.0
ApplyMixins runs each mixin against t in order and returns t.
func NewDatabaseTable ¶
NewDatabaseTable scopes the table to an explicit database.
func NewEventStoreTable ¶ added in v0.5.0
NewEventStoreTable declares the canonical ClickHouse event-store layout. Run the DDL alongside the rest of your schema.
Engine: MergeTree, ORDER BY (aggregateType, aggregateID, version). There is no auto-increment ID column in ClickHouse; the global ordering cursor is the createdAt DateTime64(9) timestamp.
func NewSnapshotTable ¶ added in v0.5.0
NewSnapshotTable declares the snapshot store. The engine is ReplacingMergeTree(version) so ClickHouse automatically merges away older snapshots, keeping only the row with the highest version per (aggregateType, aggregateID). Reads use FINAL to force an immediate merge.
func NewTable ¶
NewTable creates a table in the default database. The name is validated and a bad identifier panics at startup (see ErrInvalidIdentifier).
func (*Table) DefaultFilter ¶ added in v0.2.0
func (t *Table) DefaultFilter(e drops.Expression) *Table
DefaultFilter appends a predicate applied to every Select against the table, unless the builder is marked Unscoped().
func (*Table) OnInsert ¶ added in v0.2.0
func (t *Table) OnInsert(h InsertHook) *Table
OnInsert registers a hook invoked by InsertBuilder.WriteSQL.
func (*Table) PartitionBy ¶
func (t *Table) PartitionBy(exprs ...drops.Expression) *Table
PartitionBy sets the PARTITION BY expression(s).
func (*Table) PrimaryKey ¶
PrimaryKey sets an explicit PRIMARY KEY (defaults to ORDER BY when omitted on MergeTree-family engines).
func (*Table) SampleBy ¶
func (t *Table) SampleBy(e drops.Expression) *Table
SampleBy sets the SAMPLE BY expression.
type TimestampsCols ¶ added in v0.2.0
TimestampsCols holds the typed handles created by Timestamps.
func Timestamps ¶ added in v0.2.0
func Timestamps(t *Table) TimestampsCols
Timestamps appends "createdAt" and "updatedAt" DateTime columns defaulting to now() to t.
type TimestampsMixin ¶ added in v0.2.0
type TimestampsMixin struct {
Cols TimestampsCols
}
TimestampsMixin registers "createdAt" and "updatedAt" DateTime columns. ClickHouse has no UPDATE builder so there is no UpdateHook equivalent; both columns get DEFAULT now() so INSERT without an explicit value picks up the server clock.
func (*TimestampsMixin) Apply ¶ added in v0.2.0
func (m *TimestampsMixin) Apply(t *Table)
Apply implements Mixin.
type TreeMigration ¶ added in v0.5.0
type TreeMigration struct {
ID string
Name string
Branch string // defaults to "main"
Parents []string // empty = root; two entries = merge commit
Description string // from -- drops:description: header
Up func(ctx context.Context, db *DB) error
Down func(ctx context.Context, db *DB) error
// contains filtered or unexported fields
}
TreeMigration is one node in the migration DAG.
type TreeMigrator ¶ added in v0.5.0
type TreeMigrator struct {
// contains filtered or unexported fields
}
TreeMigrator manages a DAG of migrations tracked in the drops database.
func NewTreeMigrator ¶ added in v0.5.0
func NewTreeMigrator(db *DB) *TreeMigrator
NewTreeMigrator returns a migrator bound to db.
func (*TreeMigrator) Add ¶ added in v0.5.0
func (m *TreeMigrator) Add(mig TreeMigration) *TreeMigrator
Add registers a migration node. Panics on duplicate ID.
func (*TreeMigrator) AddFS ¶ added in v0.5.0
func (m *TreeMigrator) AddFS(fsys fs.FS, dir string) error
AddFS loads migrations from a branch-per-subdirectory layout.
Folder structure:
<dir>/
main/
main-001_create_events.up.sql
main-001_create_events.down.sql
feat/payments/
pay-001_payments_table.up.sql ← -- drops:parents: main-002
merges/
merge-001_unified.up.sql ← -- drops:parents: main-003,pay-002
Optional header: -- drops:description: one-line summary
func (*TreeMigrator) AddSQL ¶ added in v0.5.0
func (m *TreeMigrator) AddSQL(id, name, branch string, parents []string, upSQL, downSQL string) *TreeMigrator
AddSQL registers a SQL-only migration.
func (*TreeMigrator) Branches ¶ added in v0.5.0
func (m *TreeMigrator) Branches() []string
Branches returns distinct branch labels sorted lexicographically.
func (*TreeMigrator) Checkout ¶ added in v0.5.0
func (m *TreeMigrator) Checkout(ctx context.Context, id string) error
Checkout applies or rolls back to land exactly on id's ancestor set.
func (*TreeMigrator) Down ¶ added in v0.5.0
func (m *TreeMigrator) Down(ctx context.Context) error
Down rolls back all current head migrations in reverse topological order.
func (*TreeMigrator) DownTo ¶ added in v0.5.0
func (m *TreeMigrator) DownTo(ctx context.Context, id string) error
DownTo rolls back all applied descendants of id, stopping before id.
func (*TreeMigrator) Heads ¶ added in v0.5.0
func (m *TreeMigrator) Heads(ctx context.Context) ([]TreeStatus, error)
Heads returns the applied leaf nodes — current tips of all live branches.
func (*TreeMigrator) Plan ¶ added in v0.5.0
func (m *TreeMigrator) Plan(ctx context.Context) ([]PlanStep, error)
Plan returns the ordered list of migrations that Up() would apply, without touching the schema. Returns ErrTreeMigrationTampered if any applied migration's SQL has changed.
func (*TreeMigrator) Status ¶ added in v0.5.0
func (m *TreeMigrator) Status(ctx context.Context) ([]TreeStatus, error)
Status returns the full DAG status in topological order. Tampered is set true for applied migrations whose SQL has changed since apply time.
func (*TreeMigrator) Up ¶ added in v0.5.0
func (m *TreeMigrator) Up(ctx context.Context) error
Up applies every pending migration in topological order. No transaction wrapper — CH transaction support is limited. If markApplied fails after a successful Up, re-running is safe only when Up is idempotent (recommended for CH migrations).
func (*TreeMigrator) UpTo ¶ added in v0.5.0
func (m *TreeMigrator) UpTo(ctx context.Context, id string) error
UpTo applies the target and all ancestors not yet applied.
func (*TreeMigrator) Validate ¶ added in v0.5.0
func (m *TreeMigrator) Validate() error
Validate checks for cycles and missing parents without touching the DB.
func (*TreeMigrator) WithDatabase ¶ added in v0.5.0
func (m *TreeMigrator) WithDatabase(database string) *TreeMigrator
WithDatabase overrides the tracking database (default "drops").
type TreeStatus ¶ added in v0.5.0
type TreeStatus struct {
ID string
Name string
Branch string
Parents []string
Description string
Applied bool
AppliedAt time.Time
IsHead bool // applied with no applied successors
IsReady bool // unapplied, all parents applied
Checksum string
Tampered bool // stored checksum differs from current SQL
}
TreeStatus is one row produced by Status.
type UUIDPrimaryKeyCols ¶ added in v0.2.0
UUIDPrimaryKeyCols holds the typed handle created by UUIDPrimaryKey.
func UUIDPrimaryKey ¶ added in v0.2.0
func UUIDPrimaryKey(t *Table) UUIDPrimaryKeyCols
UUIDPrimaryKey appends an "id" UUID column defaulting to generateUUIDv4(). ClickHouse has no primary-key constraint; pair the column with the table's ORDER BY (or PRIMARY KEY clause on MergeTree-family engines) to make it the row identifier.
type UUIDPrimaryKeyMixin ¶ added in v0.2.0
type UUIDPrimaryKeyMixin struct {
Cols UUIDPrimaryKeyCols
}
UUIDPrimaryKeyMixin registers an "id" UUID column defaulting to generateUUIDv4(). Pair the column with the table's ORDER BY (or PRIMARY KEY clause on MergeTree-family engines) to make it the row identifier.
func (*UUIDPrimaryKeyMixin) Apply ¶ added in v0.2.0
func (m *UUIDPrimaryKeyMixin) Apply(t *Table)
Apply implements Mixin.
type Validator ¶ added in v0.2.0
Validator is called before Create / CreateMany with a pointer to each candidate row. Returning a non-nil error aborts the operation before any SQL is issued.
type Window ¶ added in v0.5.0
type Window struct {
// contains filtered or unexported fields
}
Window describes the contents of an OVER (...) clause.
func WindowSpec ¶ added in v0.5.0
func WindowSpec() *Window
WindowSpec begins building a window specification.
func (*Window) Frame ¶ added in v0.5.0
Frame sets the raw frame specification — e.g. "ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING".
func (*Window) OrderBy ¶ added in v0.5.0
func (w *Window) OrderBy(exprs ...drops.Expression) *Window
OrderBy adds ORDER BY entries to the window.
func (*Window) PartitionBy ¶ added in v0.5.0
func (w *Window) PartitionBy(exprs ...drops.Expression) *Window
PartitionBy adds PARTITION BY columns.