Golang ClickHouse
Installation
go get github.com/uptrace/go-clickhouse@latest
Connecting to ClickHouse
To connect to ClickHouse:
import "github.com/uptrace/go-clickhouse/ch" db := ch.Connect( // clickhouse://<user>:<password>@<host>:<port>/<database>?sslmode=disable ch.WithDSN("clickhouse://localhost:9000/default?sslmode=disable"), )
To see the executed queries in the console, install chdebug query hook:
import "github.com/uptrace/go-clickhouse/chdebug" db.AddQueryHook(chdebug.NewQueryHook( chdebug.WithVerbose(true), chdebug.FromEnv("CHDEBUG"), ))
Now you are ready to execute queries using database/sql API:
res, err := db.ExecContext(ctx, "SELECT 1") var num int err := db.QueryRowContext(ctx, "SELECT 1").Scan(&num)
Or using the query builder:
res, err := db.NewSelect().ColumnExpr("1").Exec(ctx) var num int err := db.NewSelect().ColumnExpr("1").Scan(ctx, &num)
Options
You can specify the following options in a DSN (connection string):
?sslmode=verify-full
- enable TLS.?sslmode=disable
- disables TLS.?dial_timeout=5s
- timeout for establishing new connections.?read_timeout=5s
- timeout for socket reads.?write_timeout=5s
- timeout for socket writes.?timeout=5s
- sets all three timeouts described above.
go-clickhouse treats all unknown options as ClickHouse query settings, for example, ?prefer_column_name_to_alias=1
enables the corresponding ClickHouse setting.
In addition to DSN, you can also use ch.Option to configure the client:
db := ch.Connect( ch.WithAddr("localhost:9000"), ch.WithTLSConfig(&tls.Config{InsecureSkipVerify: true}), ch.WithUser("test"), ch.WithPassword("test"), ch.WithDatabase("test"), ch.WithTimeout(5 * time.Second), ch.WithDialTimeout(5 * time.Second), ch.WithReadTimeout(5 * time.Second), ch.WithWriteTimeout(5 * time.Second), ch.WithQuerySettings(map[string]interface{}{ "prefer_column_name_to_alias": 1, }), )
Or use a DSN and options together:
db := ch.Connect( ch.WithDSN("clickhouse://default:@localhost:9000/uptrace?sslmode=verify-full"), ch.WithTLSConfig(tlsConfig), )
Server timezone
You can avoid a lot of confusion by configuring ClickHouse to use UTC timezone by changing config.xml
:
<?xml version="1.0" ?> <clickhouse> <timezone>UTC</timezone> </clickhouse>
Models
go-clickhouse uses struct-based models to build queries and scan results. A typical model looks like this:
type Span struct { ch.CHModel `ch:"partition:toYYYYMM(time)"` ID uint64 Text string `ch:",lc"` // low cardinality column Time time.Time `ch:",pk"` // ClickHouse primary key to be used in order by }
Having a model, you can create and drop tables:
// Create spans table. res, err := db.NewCreateTable().Model((*Span)(nil)).Exec(ctx) // Drop spans table. res, err := db.NewDropTable().Model((*Span)(nil)).Exec(ctx) // Drop table if they exist and create new tables. err := db.ResetModel(ctx, (*Model1)(nil), (*Model2)(nil))
Insert rows:
// Insert a single span. span := &Span{Name: "admin"} res, err := db.NewInsert().Model(span).Exec(ctx) // Insert multiple spans (bulk-insert). spans := []Span{span1, span2} res, err := db.NewInsert().Model(&spans).Exec(ctx)
And select rows scanning the results:
// Select a span by an id. span := new(Span) err := db.NewSelect().Model(span).Where("id = ?", 1).Scan(ctx) // Select first 10 spans. var spans []Span err := db.NewSelect().Model(&spans).OrderExpr("id ASC").Limit(10).Scan(ctx)
Scanning query results
When it comes to scanning query results, go-clickhouse is very flexible and allows scanning into structs:
span := new(Span) err := db.NewSelect().Model(span).Limit(1).Scan(ctx)
Into scalars:
var id int64 var name string err := db.NewSelect().Model((*Span)(nil)).Column("id", "name").Limit(1).Scan(ctx, &id, &name)
Into a map[string]interface{}
:
var m map[string]interface{} err := db.NewSelect().Model((*Span)(nil)).Limit(1).Scan(ctx, &m)
And into slices of the types above:
var spans []Span err := db.NewSelect().Model(&spans).Limit(1).Scan(ctx) var ids []int64 var names []string err := db.NewSelect().Model((*Span)(nil)).Column("id", "name").Limit(1).Scan(ctx, &ids, &names) var ms []map[string]interface{} err := db.NewSelect().Model((*Span)(nil)).Scan(ctx, &ms)
What's next
By now, you should have basic understanding of the API. Next, learn how to define models and write queries.