FluxaORM v2: Code-Generation-Based Go ORM for MySQL, Redis, and ClickHouse
Guide
GitHub
Guide
GitHub
    • Introduction
    • Registry
    • Data Pools
    • Entities
    • Entity Fields
    • MySQL Indexes
    • Code Generation
    • Engine
    • Context
    • Entity Schema
    • Schema Update
    • CRUD Operations
    • Async Flush
    • Search
    • Redis Search
    • MySQL Queries
    • ClickHouse Queries
    • ClickHouse Schema Management
    • Kafka
    • Debezium CDC
    • Local Cache
    • Context Cache
    • Fake Delete
    • Entity Lifecycle Callbacks
    • Metrics
    • Redis Operations
    • Distributed Lock
    • Queries Log
    • Testing

ClickHouse Schema Management

FluxaORM provides schema management for ClickHouse tables, similar to the MySQL schema update feature. Define your ClickHouse tables using a fluent builder API, register them with the registry, and use GetClickhouseAlters() to generate the DDL statements needed to synchronize your database.

Defining a ClickHouse Table

Use NewClickhouseTable() to create a table definition with a fluent builder:

import fluxaorm "github.com/latolukasz/fluxaorm/v2"

table := fluxaorm.NewClickhouseTable("events", "analytics").
    Column("id", "UInt64").
    Column("event_name", "String").
    Column("user_id", "UInt64").
    Column("ts", "DateTime").
    ColumnDefault("processed", "UInt8", "0").
    ColumnCodec("payload", "String", "ZSTD(1)").
    Engine("MergeTree").
    OrderBy("id", "ts").
    PartitionBy("toYYYYMM(ts)").
    TTL("ts + INTERVAL 90 DAY").
    Setting("index_granularity", "8192")

Registering Tables

Register ClickHouse table definitions with the registry before calling Validate():

registry := fluxaorm.NewRegistry()
registry.RegisterClickhouse("clickhouse://localhost:9000/default", "analytics", nil)
registry.RegisterClickhouseTable(
    fluxaorm.NewClickhouseTable("events", "analytics").
        Column("id", "UInt64").
        Column("event_name", "String").
        Column("ts", "DateTime").
        Engine("MergeTree").
        OrderBy("id", "ts"),
)
engine, err := registry.Validate()

Builder API Reference

Column Methods

All column methods return *ClickhouseTableBuilder for fluent chaining:

MethodDescription
Column(name, typeName)Simple column
ColumnDefault(name, typeName, defaultExpr)Column with DEFAULT expression
ColumnMaterialized(name, typeName, expr)Column with MATERIALIZED expression
ColumnAlias(name, typeName, expr)Column with ALIAS expression
ColumnCodec(name, typeName, codec)Column with compression CODEC
ColumnTTL(name, typeName, ttl)Column with column-level TTL
ColumnComment(name, typeName, comment)Column with a comment
ColumnFull(name, typeName, opts)Column with all options via ClickhouseColumnOptions

ClickhouseColumnOptions

Used with ColumnFull() to set multiple column options at once:

type ClickhouseColumnOptions struct {
    Default      string // DEFAULT expression
    Materialized string // MATERIALIZED expression
    Alias        string // ALIAS expression
    Codec        string // e.g. "ZSTD(1)"
    TTL          string // column-level TTL expression
    Comment      string
}

Table-Level Methods

MethodDescription
Engine(engine)Table engine, e.g. "MergeTree", "ReplacingMergeTree(Version)"
OrderBy(columns...)ORDER BY columns (required)
PartitionBy(expr)PARTITION BY expression
PrimaryKey(columns...)PRIMARY KEY columns (defaults to ORDER BY if not set)
TTL(expr)Table-level TTL expression
Setting(key, value)Add a SETTINGS key=value pair
Comment(comment)Table comment

Getting Schema Alters

Use GetClickhouseAlters() to compare registered table definitions with the actual ClickHouse database and get the DDL statements needed:

ctx := engine.NewContext(context.Background())

alters, err := fluxaorm.GetClickhouseAlters(ctx)
if err != nil {
    panic(err)
}
for _, alter := range alters {
    fmt.Println(alter.SQL)  // e.g. "CREATE TABLE events ..."
    fmt.Println(alter.Pool) // e.g. "analytics"
}

Each fluxaorm.ClickhouseAlter has the following fields:

FieldTypeDescription
SQLstringThe DDL statement to execute
PoolstringThe ClickHouse pool code this alter belongs to

To execute all alters:

for _, alter := range alters {
    err = alter.Exec(ctx)
    if err != nil {
        panic(err)
    }
}

What Gets Compared

GetClickhouseAlters() generates the following types of DDL:

ScenarioGenerated DDL
Table not in databaseCREATE TABLE ...
New columnALTER TABLE ... ADD COLUMN ...
Changed column (type, default, codec, comment)ALTER TABLE ... MODIFY COLUMN ...
Removed columnALTER TABLE ... DROP COLUMN ...
Table in database but not registeredDROP TABLE IF EXISTS ...
TTL defined in builderALTER TABLE ... MODIFY TTL ...
SETTINGS defined in builderALTER TABLE ... MODIFY SETTING ...
COMMENT defined in builderALTER TABLE ... MODIFY COMMENT ...
ENGINE or ORDER BY mismatchWarning comment (manual recreation required)

Warning

ENGINE, ORDER BY, and PARTITION BY cannot be altered in ClickHouse. If these differ between the registered definition and the actual table, GetClickhouseAlters() returns a SQL comment describing the mismatch. You must manually recreate the table to fix these.

Warning

FluxaORM generates DROP TABLE ... queries for all tables in the ClickHouse database that are not registered. See ignored tables for how to protect tables from being dropped.

Full Example

package main

import (
    "context"
    "fmt"

    "github.com/latolukasz/fluxaorm/v2"
)

func main() {
    registry := fluxaorm.NewRegistry()
    registry.RegisterClickhouse("clickhouse://localhost:9000/analytics", "analytics", nil)

    // Define tables
    registry.RegisterClickhouseTable(
        fluxaorm.NewClickhouseTable("events", "analytics").
            Column("id", "UInt64").
            Column("event_name", "String").
            Column("user_id", "UInt64").
            Column("ts", "DateTime").
            ColumnDefault("processed", "UInt8", "0").
            Engine("MergeTree").
            OrderBy("id", "ts").
            PartitionBy("toYYYYMM(ts)").
            TTL("ts + INTERVAL 90 DAY"),
    )

    registry.RegisterClickhouseTable(
        fluxaorm.NewClickhouseTable("metrics", "analytics").
            Column("id", "UInt64").
            Column("name", "String").
            Column("value", "Float64").
            Column("ts", "DateTime").
            ColumnCodec("value", "Float64", "Gorilla").
            Engine("MergeTree").
            OrderBy("id"),
    )

    engine, err := registry.Validate()
    if err != nil {
        panic(err)
    }

    ctx := engine.NewContext(context.Background())

    // Get and apply alters
    alters, err := fluxaorm.GetClickhouseAlters(ctx)
    if err != nil {
        panic(err)
    }
    for _, alter := range alters {
        fmt.Println(alter.SQL)
        err = alter.Exec(ctx)
        if err != nil {
            panic(err)
        }
    }
}

Tips

Call GetClickhouseAlters() alongside GetAlters() and GetRedisSearchAlters() in your migration flow to keep all data stores in sync.

Edit this page
Last Updated: 3/19/26, 3:49 PM
Prev
ClickHouse Queries
Next
Kafka