db/clickhouse
A ClickHouse client over the HTTP interface: statements, typed rows, batch inserts and the shared db interface.
import db.clickhouse
ClickHouse is a column oriented database for analytics: it scans billions of rows fast, and takes inserts in large batches. This package talks to it over its HTTP interface (port 8123, or 8443 with TLS) with net.http, so there is no driver library to install and no native protocol to speak. Results arrive as JSONCompact, with 64 bit integers quoted so that none of them loses digits.
Open a connection with Open or OpenURL, send statements with Exec, read rows with Query, and load many rows at once with a batch. Values go into the SQL through the argument functions, such as StrArg and IntArg, which escape them, or through ClickHouse's own typed {name:Type} placeholders. AsDb adapts a connection to the shared db interface. A statement that fails behind a 200 status, because the server hit an exception while already streaming the reply, is caught too: it shows in Error and Code like any other failure.
The package does not speak the native TCP protocol or RowBinary, does not compress, and has no SQL dialect in db/sql yet. Each page section below is one part of the API, and each has a runnable example.
Put values into SQL safely
Never build SQL by joining strings. Make each value with an argument function, which quotes and escapes it, and let Bind put the values into the ? places in order. Bind does not run anything, so it is also a good way to see the final text. A ? inside quotes or a comment is not a place, and when the number of values differs from the number of places the result is empty.
import db.clickhouse
func main:
mut args := Vector {} as Vector<str>
args.push(clickhouse.StrArg("o'brien"))
args.push(clickhouse.IntArg(42 as i64))
println clickhouse.Bind("SELECT * FROM users WHERE name = ? AND age > ?", args)
mut one := Vector {} as Vector<str>
one.push(clickhouse.IntArg(7 as i64))
println clickhouse.Bind("SELECT '?' AS mark, ? /* ? */", one)
println clickhouse.QuoteIdent("my table")
println clickhouse.Bind("SELECT ?", args).len()
endThe quote in o'brien is escaped for you. Table and column names are not values, so they go through QuoteIdent. Exec and Query do the same binding when you give them the values.
Connect and create a table
Start from DefaultOptions (a server on this machine, user default) and change what differs. Open sends nothing yet, so call Ping to see whether the server answers. Exec runs a statement that returns no rows and gives true on success; on failure Error and Code say why.
import db.clickhouse
func main:
mut o := clickhouse.DefaultOptions()
o.addr = "127.0.0.1:8123"
o.database = "default"
o.username = "default"
mut c := clickhouse.Open(o)
defer c.Close()
if not c.Ping():
println "no ClickHouse at", o.addr
ret 1
end
println c.ServerVersion()
none := Vector {} as Vector<str>
ok := c.Exec("CREATE TABLE IF NOT EXISTS visits (day Date, page String, hits UInt32) ENGINE = MergeTree ORDER BY (day, page)", none)
if not ok:
println "create failed:", c.Code(), c.Error()
ret 1
end
println "table ready"
endThe table uses the MergeTree engine, the usual one, and its ORDER BY is the sort key that makes queries on day and page fast. ClickHouse has no auto increment key and no transactions to wait for; a statement is done when Exec returns.
Insert in batches
ClickHouse likes a few large inserts, not many small ones. PrepareBatch takes the INSERT INTO table (columns) part, Append adds one row of ready SQL values, and SendBatch sends all the rows in one request. Append returns false if the row has a different number of values than there are columns.
import db.clickhouse
func row(day: str, page: str, hits: i64): Vector<str>:
mut r := Vector {} as Vector<str>
r.push(clickhouse.StrArg(day))
r.push(clickhouse.StrArg(page))
r.push(clickhouse.IntArg(hits))
ret r
end
func main:
mut c := clickhouse.Open(clickhouse.DefaultOptions())
defer c.Close()
mut b := c.PrepareBatch("INSERT INTO visits (day, page, hits)")
if b.Error().len() > 0:
println b.Error()
ret 1
end
b.Append(row("2026-11-01", "/", 120 as i64))
b.Append(row("2026-11-01", "/docs", 45 as i64))
b.Append(row("2026-11-02", "/", 98 as i64))
println b.Len(), "rows queued"
if not c.SendBatch(b):
println "insert failed:", c.Error()
ret 1
end
println "inserted"
endHundreds or thousands of rows per batch is a good size to start with. A batch with no rows, or one that already has an error, is not sent.
Read the rows back
Query returns a Rows. Check Ok, then call Next in a loop and read the current row by column number with Text, Int64 and the other readers; Close it when done. The loop is until rows.Next(): because until runs while its condition is true. For a value that comes from outside, the typed placeholders {name:Type} of QueryParams let the server check the type.
import db.clickhouse
func main:
mut c := clickhouse.Open(clickhouse.DefaultOptions())
defer c.Close()
mut args := Vector {} as Vector<str>
args.push(clickhouse.StrArg("2026-11-01"))
mut rows := c.Query("SELECT page, sum(hits) AS total FROM visits WHERE day >= ? GROUP BY page ORDER BY total DESC", args)
if not rows.Ok():
println rows.Error()
ret 1
end
until rows.Next():
println rows.Text(0), rows.Int64(1)
end
rows.Close()
mut params := HashMap {} as HashMap<str, str>
params.put("page", "/docs")
mut one := c.QueryParams("SELECT sum(hits) FROM visits WHERE page = {page:String}", params)
until one.Next():
println "docs:", one.Int64(0)
end
one.Close()
endAn aggregate such as sum(hits) is a 64 bit integer in ClickHouse, which arrives quoted in the JSON and is read back without losing digits by Int64.
Types
Options
struct Options| Name | Type | Description |
|---|---|---|
addr | str = "localhost:8123" | Host and port, host:port; a leading http:// or https:// is accepted. The port defaults to 8123, or 8443 when secure. |
database | str = "" | The database every statement runs in; empty means the user's default. |
username | str = "default" | The user name, sent as X-ClickHouse-User. The default is default. |
password | str = "" | The password, sent as X-ClickHouse-Key. |
secure | bool = false | Use HTTPS. |
timeout_ms | int = 30000 | How long a request may take, in milliseconds. |
settings | HashMap | ClickHouse settings sent with every request, such as max_threads. |
headers | HashMap | Extra HTTP headers for every request. |
import db.clickhouse
func main:
mut o := clickhouse.DefaultOptions()
o.addr = "127.0.0.1:8123"
o.database = "shop"
o.username = "default"
o.password = ""
o.secure = false
o.timeout_ms = 10000
o.settings.put("max_threads", "4")
o.headers.put("X-Request-Source", "docs")
println clickhouse.DEFAULT_PORT, clickhouse.DEFAULT_SECURE_PORT
mut c := clickhouse.Open(o)
if not c.Ping():
println "no server:", c.Error(), c.status
ret
end
println c.ServerVersion(), c.base, c.connected
byurl := clickhouse.OpenURL("clickhouse://default:@127.0.0.1:8123/shop?max_threads=4")
println byurl.Ok(), byurl.opts.database
none := Vector {} as Vector<str>
println c.Exec("CREATE TABLE IF NOT EXISTS events (id UInt64, name String) ENGINE = MergeTree ORDER BY id", none)
mut ins := HashMap {} as HashMap<str, str>
ins.put("id", "1")
ins.put("name", "first")
println c.ExecParams("INSERT INTO events VALUES ({id:UInt64}, {name:String})", ins)
println c.ExecWith("OPTIMIZE TABLE events", none, o.settings)
mut pn := HashMap {} as HashMap<str, str>
pn.put("n", "7")
println c.ExecFull("SELECT {n:UInt8}", pn, HashMap {} as HashMap<str, str>)
if not c.Ok():
println "failed with code", c.Code(), c.Error()
end
println c.query_id, c.read_rows, c.written_rows
c.Close()
endConn
struct ConnOk is false and Error and Code say why; the next call clears them.| Name | Type | Description |
|---|---|---|
opts | Options | The options the connection was opened with. |
base | str = "" | The base URL, such as http://127.0.0.1:8123. |
connected | bool = false | False after Close or when the address was invalid. |
status | int = 0 | The HTTP status of the last reply; 0 when no reply arrived. |
err | str = "" | The last error text. |
code | int = 0 | The ClickHouse exception code of the last failure, 0 if none. |
query_id | str = "" | The query id the server gave the last request. |
read_rows | i64 = 0 | Rows the last request read, from the server's summary header. |
written_rows | i64 = 0 | Rows the last request wrote, from the server's summary header. |
The example for this item is shown with the ones above: db-clickhouse-Connect.salam
Ok
func Ok(): boolThe example for this item is shown with the ones above: db-clickhouse-Connect.salam
Error
func Error(): strThe example for this item is shown with the ones above: db-clickhouse-Connect.salam
Code
func Code(): intX-ClickHouse-Exception-Code header or the Code: N text of the body.The example for this item is shown with the ones above: db-clickhouse-Connect.salam
Close
func Close()The example for this item is shown with the ones above: db-clickhouse-Connect.salam
Ping
func Ping(): bool/ping; true when the server answers.The example for this item is shown with the ones above: db-clickhouse-Connect.salam
Exec
func Exec(sql: str, args: Vector<str>): bool? is replaced by the next value of args, built with StrArg, IntArg and the other argument functions. Question marks inside quotes and comments are left alone.The example for this item is shown with the ones above: db-clickhouse-Connect.salam
ExecParams
func ExecParams(sql: str, params: HashMap<str, str>): bool{name:Type}, sent as param_name query parameters.The example for this item is shown with the ones above: db-clickhouse-Connect.salam
ExecWith
func ExecWith(sql: str, args: Vector<str>, settings: HashMap<str, str>): boolExec, with ClickHouse settings for this call only, added to those in the options.The example for this item is shown with the ones above: db-clickhouse-Connect.salam
ExecFull
func ExecFull(sql: str, params: HashMap<str, str>, extra: HashMap<str, str>): boolThe example for this item is shown with the ones above: db-clickhouse-Connect.salam
Query
func Query(sql: str, args: Vector<str>): Rows? placeholders as in Exec. Check Ok on the result, and call Close when done.import db.clickhouse
func main:
mut c := clickhouse.Open(clickhouse.DefaultOptions())
defer c.Close()
none := Vector {} as Vector<str>
mut args := Vector {} as Vector<str>
args.push(clickhouse.UIntArg(10 as u64))
mut rows := c.Query("SELECT id, name, price, tags, active FROM events WHERE id >= ?", args)
if not rows.Ok():
println rows.Error()
ret
end
println rows.ColumnCount(), rows.RowCount(), rows.total
repeat rows.ColumnCount() in i:
println i, rows.ColumnName(i), rows.ColumnType(i), rows.ColumnKind(i), rows.ColumnIndex(rows.ColumnName(i))
end
until rows.Next():
println rows.UInt64(0), rows.Int64(0), rows.Int(0), rows.Text(1), rows.Float(2), rows.Bool(4)
println rows.IsNull(1), rows.Raw(3), rows.List(3).len(), rows.Row().len()
println rows.TextBy("name"), rows.Int64By("id"), rows.FloatBy("price")
end
println rows.pos, rows.names.len(), rows.types.len(), rows.rows.len(), rows.cells.len()
rows.Close()
mut params := HashMap {} as HashMap<str, str>
params.put("min", "10")
mut byname := c.QueryParams("SELECT count() FROM events WHERE id >= {min:UInt64}", params)
println byname.Ok()
byname.Close()
mut limits := HashMap {} as HashMap<str, str>
limits.put("max_execution_time", "5")
mut limited := c.QueryWith("SELECT sum(id) FROM events", none, limits)
println limited.Ok()
limited.Close()
mut full := c.QueryFull("SELECT {min:UInt64}", params, limits)
println full.Ok()
full.Close()
first := c.QueryRow("SELECT id, name FROM events ORDER BY id LIMIT 1", none)
if first.len() == 2:
println first.get(0), first.get(1)
end
println c.QueryText("SELECT version()"), c.QueryInt("SELECT count() FROM events")
endQueryParams
func QueryParams(sql: str, params: HashMap<str, str>): Rows{name:Type} placeholders.The example for this item is shown with the ones above: db-clickhouse-Query.salam
QueryWith
func QueryWith(sql: str, args: Vector<str>, settings: HashMap<str, str>): RowsQuery, with settings for this call only.The example for this item is shown with the ones above: db-clickhouse-Query.salam
QueryRow
func QueryRow(sql: str, args: Vector<str>): Vector<str>The example for this item is shown with the ones above: db-clickhouse-Query.salam
QueryFull
func QueryFull(sql: str, params: HashMap<str, str>, extra: HashMap<str, str>): RowsThe example for this item is shown with the ones above: db-clickhouse-Query.salam
QueryText
func QueryText(sql: str): strThe example for this item is shown with the ones above: db-clickhouse-Query.salam
QueryInt
func QueryInt(sql: str): i64The example for this item is shown with the ones above: db-clickhouse-Query.salam
ServerVersion
func ServerVersion(): strSELECT version().The example for this item is shown with the ones above: db-clickhouse-Connect.salam
PrepareBatch
func PrepareBatch(sql: str): BatchINSERT INTO table (columns); a trailing VALUES or semicolon is dropped. Anything else gives a batch whose Error says why.import db.clickhouse
func main:
mut c := clickhouse.Open(clickhouse.DefaultOptions())
defer c.Close()
mut b := c.PrepareBatch("INSERT INTO events (id, name, tags, score)")
if b.Error().len() > 0:
println b.Error()
ret
end
println b.head, b.width
mut tags := Vector {} as Vector<str>
tags.push(clickhouse.StrArg("a"))
tags.push(clickhouse.StrArg("b"))
mut row := Vector {} as Vector<str>
row.push(clickhouse.IntArg(1 as i64))
row.push(clickhouse.StrArg("o'brien"))
row.push(clickhouse.ArrayArg(tags))
row.push(clickhouse.FloatArg(2.5))
println b.Append(row)
again := clickhouse.Args(row)
println b.Append(again)
println c.SendBatch(b)
mut broken := c.PrepareBatch("INSERT INTO events (id, name, tags, score)")
mut bad := Vector {} as Vector<str>
bad.push(clickhouse.NullArg())
println broken.Append(bad), broken.Error(), broken.Len(), broken.rows.len()
broken.Abort()
keys := clickhouse.Args(tags)
println clickhouse.MapArg(keys, keys)
println clickhouse.TupleArg(keys), clickhouse.BoolArg(true), clickhouse.RawArg("now()")
println clickhouse.QuoteIdent("my table")
endSendBatch
func SendBatch(b: Batch): boolINSERT; false when the batch is empty or broken, or the server refused it.The example for this item is shown with the ones above: db-clickhouse-Batch.salam
Rows
struct RowsNext, then read the current row by column number or name.| Name | Type | Description |
|---|---|---|
names | Vector | The column names. |
types | Vector | The column types as ClickHouse writes them, such as Nullable(String). |
rows | Vector | Every row, each as JSON text. |
cells | Vector | The values of the current row as JSON text. |
pos | int = -1 | The index of the current row; -1 before the first Next. |
total | i64 = 0 | The row count the server reported. |
err | str = "" | The error text; empty when the query worked. |
The example for this item is shown with the ones above: db-clickhouse-Query.salam
Ok
func Ok(): boolThe example for this item is shown with the ones above: db-clickhouse-Query.salam
Error
func Error(): strThe example for this item is shown with the ones above: db-clickhouse-Query.salam
Next
func Next(): boolThe example for this item is shown with the ones above: db-clickhouse-Query.salam
RowCount
func RowCount(): intThe example for this item is shown with the ones above: db-clickhouse-Query.salam
ColumnCount
func ColumnCount(): intThe example for this item is shown with the ones above: db-clickhouse-Query.salam
ColumnName
func ColumnName(col: int): strThe example for this item is shown with the ones above: db-clickhouse-Query.salam
ColumnType
func ColumnType(col: int): strThe example for this item is shown with the ones above: db-clickhouse-Query.salam
ColumnKind
func ColumnKind(col: int): intKIND_... group of a column's type.The example for this item is shown with the ones above: db-clickhouse-Query.salam
ColumnIndex
func ColumnIndex(name: str): intThe example for this item is shown with the ones above: db-clickhouse-Query.salam
Raw
func Raw(col: int): strThe example for this item is shown with the ones above: db-clickhouse-Query.salam
IsNull
func IsNull(col: int): boolNULL.The example for this item is shown with the ones above: db-clickhouse-Query.salam
Text
func Text(col: int): strThe example for this item is shown with the ones above: db-clickhouse-Query.salam
Int64
func Int64(col: int): i64The example for this item is shown with the ones above: db-clickhouse-Query.salam
Int
func Int(col: int): intThe example for this item is shown with the ones above: db-clickhouse-Query.salam
UInt64
func UInt64(col: int): u64The example for this item is shown with the ones above: db-clickhouse-Query.salam
Float
func Float(col: int): f64nan and inf are understood.The example for this item is shown with the ones above: db-clickhouse-Query.salam
Bool
func Bool(col: int): boolThe example for this item is shown with the ones above: db-clickhouse-Query.salam
List
func List(col: int): Vector<str>The example for this item is shown with the ones above: db-clickhouse-Query.salam
TextBy
func TextBy(name: str): strText, by column name.The example for this item is shown with the ones above: db-clickhouse-Query.salam
Int64By
func Int64By(name: str): i64Int64, by column name.The example for this item is shown with the ones above: db-clickhouse-Query.salam
FloatBy
func FloatBy(name: str): f64Float, by column name.The example for this item is shown with the ones above: db-clickhouse-Query.salam
Row
func Row(): Vector<str>The example for this item is shown with the ones above: db-clickhouse-Query.salam
Close
func Close()The example for this item is shown with the ones above: db-clickhouse-Query.salam
Batch
struct BatchAppend it, then send the batch with SendBatch.| Name | Type | Description |
|---|---|---|
head | str = "" | The INSERT INTO table (columns) part. |
width | int = 0 | The number of columns the insert names; 0 when it names none. |
rows | Vector | The rows added so far, each already formatted as SQL. |
err | str = "" | The error text; empty when the batch is fine. |
The example for this item is shown with the ones above: db-clickhouse-Batch.salam
Append
func Append(values: Vector<str>): boolError set, when the count differs from the number of columns.The example for this item is shown with the ones above: db-clickhouse-Batch.salam
Len
func Len(): intThe example for this item is shown with the ones above: db-clickhouse-Batch.salam
Error
func Error(): strThe example for this item is shown with the ones above: db-clickhouse-Batch.salam
Abort
func Abort()The example for this item is shown with the ones above: db-clickhouse-Batch.salam
Constants and variables
DEFAULT_PORT
const DEFAULT_PORT: i32 = 8123The example for this item is shown with the ones above: db-clickhouse-Connect.salam
DEFAULT_SECURE_PORT
const DEFAULT_SECURE_PORT: i32 = 8443secure is on and the address has no port.The example for this item is shown with the ones above: db-clickhouse-Connect.salam
KIND_OTHER
const KIND_OTHER: i32 = 0import db.clickhouse
func main:
mut args := Vector {} as Vector<str>
args.push(clickhouse.IntArg(7 as i64))
args.push(clickhouse.StrArg("it's"))
println clickhouse.Bind("SELECT ? AS id, ? AS name", args)
t := "LowCardinality(Nullable(String))"
println clickhouse.BaseType(t), clickhouse.IsNullableType(t)
println clickhouse.TypeKind("Array(Int32)") == clickhouse.KIND_ARRAY
println clickhouse.ElementType("Array(Nullable(Int32))")
println clickhouse.KIND_OTHER, clickhouse.KIND_INT, clickhouse.KIND_UINT, clickhouse.KIND_FLOAT, clickhouse.KIND_DECIMAL, clickhouse.KIND_STRING
println clickhouse.KIND_BOOL, clickhouse.KIND_DATE, clickhouse.KIND_DATETIME, clickhouse.KIND_MAP, clickhouse.KIND_TUPLE
endKIND_INT
const KIND_INT: i32 = 1Int8 to Int256.The example for this item is shown with the ones above: db-clickhouse-Types.salam
KIND_UINT
const KIND_UINT: i32 = 2UInt8 to UInt256.The example for this item is shown with the ones above: db-clickhouse-Types.salam
KIND_FLOAT
const KIND_FLOAT: i32 = 3Float32 or Float64.The example for this item is shown with the ones above: db-clickhouse-Types.salam
KIND_DECIMAL
const KIND_DECIMAL: i32 = 4Decimal.The example for this item is shown with the ones above: db-clickhouse-Types.salam
KIND_STRING
const KIND_STRING: i32 = 5String, FixedString, UUID, Enum, IPv4, IPv6.The example for this item is shown with the ones above: db-clickhouse-Types.salam
KIND_BOOL
const KIND_BOOL: i32 = 6Bool.The example for this item is shown with the ones above: db-clickhouse-Types.salam
KIND_DATE
const KIND_DATE: i32 = 7Date or Date32.The example for this item is shown with the ones above: db-clickhouse-Types.salam
KIND_DATETIME
const KIND_DATETIME: i32 = 8DateTime or DateTime64.The example for this item is shown with the ones above: db-clickhouse-Types.salam
KIND_ARRAY
const KIND_ARRAY: i32 = 9Array or Nested.The example for this item is shown with the ones above: db-clickhouse-Types.salam
KIND_MAP
const KIND_MAP: i32 = 10Map.The example for this item is shown with the ones above: db-clickhouse-Types.salam
KIND_TUPLE
const KIND_TUPLE: i32 = 11Tuple.The example for this item is shown with the ones above: db-clickhouse-Types.salam
Functions
StrArg
func StrArg(s: str): strThe example for this item is shown with the ones above: db-clickhouse-Types.salam
QuoteIdent
func QuoteIdent(name: str): strThe example for this item is shown with the ones above: db-clickhouse-Batch.salam
IntArg
func IntArg(n: i64): strThe example for this item is shown with the ones above: db-clickhouse-Batch.salam
UIntArg
func UIntArg(n: u64): strThe example for this item is shown with the ones above: db-clickhouse-Query.salam
FloatArg
func FloatArg(x: f64): strnan, inf and -inf are written as ClickHouse reads them.The example for this item is shown with the ones above: db-clickhouse-Batch.salam
BoolArg
func BoolArg(b: bool): strtrue or false.The example for this item is shown with the ones above: db-clickhouse-Batch.salam
NullArg
func NullArg(): strNULL.The example for this item is shown with the ones above: db-clickhouse-Batch.salam
RawArg
func RawArg(sql: str): strnow(). Never use it with user input.The example for this item is shown with the ones above: db-clickhouse-Batch.salam
ArrayArg
func ArrayArg(items: Vector<str>): strThe example for this item is shown with the ones above: db-clickhouse-Batch.salam
TupleArg
func TupleArg(items: Vector<str>): strThe example for this item is shown with the ones above: db-clickhouse-Batch.salam
MapArg
func MapArg(keys: Vector<str>, values: Vector<str>): strmap(...) literal from keys and values that are already SQL literals; the shorter list decides the size.The example for this item is shown with the ones above: db-clickhouse-Batch.salam
Args
func Args(items: Vector<str>): Vector<str>The example for this item is shown with the ones above: db-clickhouse-Batch.salam
Bind
func Bind(sql: str, args: Vector<str>): str? places of the SQL and returns the text, without running it. Empty when the counts differ.The example for this item is shown with the ones above: db-clickhouse-Types.salam
BaseType
func BaseType(t: str): strNullable(...) and LowCardinality(...) taken off.The example for this item is shown with the ones above: db-clickhouse-Types.salam
IsNullableType
func IsNullableType(t: str): boolNullable, possibly inside LowCardinality.The example for this item is shown with the ones above: db-clickhouse-Types.salam
TypeKind
func TypeKind(t: str): intKIND_... group of a type.The example for this item is shown with the ones above: db-clickhouse-Types.salam
ElementType
func ElementType(t: str): strArray(...); empty for anything else.The example for this item is shown with the ones above: db-clickhouse-Types.salam
DefaultOptions
func DefaultOptions(): Optionslocalhost:8123, user default, no password.The example for this item is shown with the ones above: db-clickhouse-Connect.salam
Open
func Open(o: Options): ConnPing checks that the server is there.The example for this item is shown with the ones above: db-clickhouse-Connect.salam
OpenURL
func OpenURL(dsn: str): Connclickhouse://user:pass@host:8123/db?secure=true&max_threads=4. clickhouses://, http:// and https:// also work. database, username, password, secure and timeout set options; any other key becomes a ClickHouse setting.The example for this item is shown with the ones above: db-clickhouse-Connect.salam
AsDb
func AsDb(c: Conn): dyn db.Connectiondyn db.Connection, so code written against the shared db interface runs on ClickHouse too. Exec returns 0 or -1, Changes is the rows the last request wrote, LastInsertId is always 0, and a transaction runs each statement at once: ClickHouse has none, so Commit returns 0 and Rollback returns -1.import db
import db.clickhouse
func total(c: dyn db.Connection, table: str): int:
ret db.QueryInt(c, "SELECT count() FROM " + table)
end
func main:
c := clickhouse.Open(clickhouse.DefaultOptions())
d := clickhouse.AsDb(c)
defer d.Close()
println d.Kind() == db.CLICKHOUSE, d.Ping()
println total(d, "events")
r := d.Query("SELECT id, name FROM events LIMIT 5")
until r.Next():
println r.Int64(0), r.Text(1), r.ColumnType(0)
end
r.Close()
t := d.Begin()
println t.Exec("INSERT INTO events VALUES (99, 'x')"), t.Commit()
end