一个ClickHouse的驱动库
Dependencies
import {
"moonbitlang/async@0.20.1",
}import {
"moonbitlang/async/http",
"moonbitlang/core/buffer",
"moonbitlang/core/encoding/base64",
"moonbitlang/core/encoding/utf8",
"liuhuo23/clickhouse-driver" @lib,
}///|
async fn main {
let conn = @lib.connect(
host="127.0.0.1",
port=8123,
user="default",
password="",
database="default",
client_name="my-app",
)
defer conn.close()
// 1. Health check
conn.ping()
// 2. SELECT — columns come back automatically
let result = conn.execute_query(
"SELECT id, name FROM users WHERE created_at > {lo: DateTime} LIMIT {n: UInt32}",
params=Map::from_array([("lo", "2024-01-01 00:00:00"), ("n", "10")]),
)
// 3. Inspect schema
for col in result.columns {
println(col.name + " : " + col.type_)
}
// 4. Iterate rows (each cell is a string)
for row in result.rows {
println(row.values)
}
// 5. Or convert to row maps keyed by column name
for m in result.to_map() {
println(m["name"])
}
// 6. INSERT via inline VALUES — all values come from params
ignore(
conn.execute_query(
"INSERT INTO users (id, name) VALUES " +
"({id: UInt32}, {name: String}), ({id2: UInt32}, {name2: String})",
params=Map::from_array([
("id", "1"),
("name", "alice"),
("id2", "2"),
("name2", "bob"),
]),
),
)
}pub fn connect(
host~ : String,
port~ : Int,
user~ : String,
password~ : String,
database~ : String,
client_name~ : String,
timeout_ms? : Int = 0,
https? : Bool = false,
skip_verify? : Bool = false,
compress? : Bool = false,
) -> Connection| Parameter | Type | Description |
|---|---|---|
| host | String | Server hostname or IP |
| port | Int | HTTP port (default 8123) |
| user | String | Username |
| password | String | Password |
| database | String | Default database |
| client_name | String | Sent via X-ClickHouse-Client-Name header |
| timeout_ms | Int | Per-request timeout in ms; 0 = no timeout |
| https | Bool | Use TLS (https://); default false |
| skip_verify | Bool | Skip TLS cert verification; default false |
| compress | Bool | Request ClickHouse LZ4 compression; default false |
///|
pub struct Connection {
host : String
port : Int
user : String
password : String
database : String
client_name : String
timeout_ms : Int
https : Bool
skip_verify : Bool
compress : Bool
}pub async fn ping(self : Connection) -> Unit raisepub async fn execute_query(
self : Connection,
sql : String,
params? : Map[String, String] = {},
) -> ResultSet raise// No params
let r = conn.execute_query("SELECT version()")
// Single param
let r = conn.execute_query(
"SELECT * FROM events WHERE id = {id: UInt64}",
params=Map::from_array([("id", "42")]),
)
// Multiple params, used in INSERT VALUES
ignore(conn.execute_query(
"INSERT INTO events (id, ts, msg) VALUES " +
"({id: UInt64}, {ts: DateTime}, {msg: String})",
params=Map::from_array([
("id", "1"),
("ts", "2024-01-01 00:00:00"),
("msg", "hello"),
]),
))pub async fn insert(
self : Connection,
table : String,
columns : Array[String],
rows : Array[Array[String]],
format? : InsertFormat = Tsv,
) -> Unit raiseconn.insert("users", ["id", "name"], [["1", "alice"], ["2", "bob"]])pub async fn execute_stream(
self : Connection,
sql : String,
params? : Map[String, String] = {},
) -> ResultSetCursor raiselet cur = conn.execute_stream("SELECT * FROM events")
defer cur.close()
while true {
match cur.next() {
None => break
Some(row) => println(row.values)
}
}let pool = conn.pool(size=4) // up to 4 concurrent connections
defer pool.close()
pool.ping() // same API as Connection
let r = pool.execute_query("SELECT count() FROM events")
pool.insert("events", ["id", "ts"], [["1", "2024-01-01 00:00:00"]])
let cur = pool.execute_stream("SELECT * FROM events") // cursor returns the
defer cur.close() // connection on closepub async fn ConnectionPool::ping(Self) -> Unit
pub async fn ConnectionPool::execute_query(Self, String, params? : Map[String, String]) -> ResultSet
pub async fn ConnectionPool::execute(Self, String, Array[String]) -> ResultSet
pub async fn ConnectionPool::insert(Self, String, Array[String], Array[Array[String]], format? : InsertFormat) -> Unit
pub async fn ConnectionPool::execute_stream(Self, String, params? : Map[String, String]) -> ResultSetCursor
pub fn ConnectionPool::max_size(Self) -> Int
pub fn ConnectionPool::close(Self) -> Unitpub async fn cancel(self : Connection) -> Unitpub fn close(self : Connection) -> Unitlet conn = @lib.connect(...)
defer conn.close()///|
pub struct ResultSet {
columns : Array[Column]
rows : Array[Row]
}pub fn to_map(self : ResultSet) -> Array[Map[String, String]]for m in result.to_map() {
let name = m.get_or_default("name", "")
println(name)
}///|
pub struct Row {
values : Array[String]
}///|
pub struct Column {
name : String
type_ : String
}try {
conn.execute_query("SELECT * FROM no_such_table")
} catch {
@lib.DbError::ServerError(code~, name=_, message~) =>
println("server error: code=" + code.to_string() + " " + message)
@lib.DbError::ConnectionError(msg) =>
println("connection error: " + msg)
_ => println("other error")
}///|
pub suberror DbError {
ServerError(code~ : Int, name~ : String, message~ : String)
ConnectionError(String)
} derive(Show)| Variant | Fields | When |
|---|---|---|
| ServerError | code : Int, name : String, message : String | Server returned a non-2xx HTTP response with an error body. |
| ConnectionError | String | Network / I/O error (connection refused, malformed response, …). |
POST /?database=<db>&default_format=TabSeparatedWithNamesAndTypes
&query=<url-encoded SQL>
[¶m_<key>=<url-encoded value>...]
HTTP/1.1
Host: <host>:<port>
Authorization: Basic <base64(user:password)>
X-ClickHouse-Client-Name: <client_name>
Content-Length: 0<col1>\t<col2>\t<col3>
<Type1>\t<Type2>\t<Type3>
<val1>\t<val2>\t<val3>
<val4>\t<val5>\t<val6>
...# Default connection (127.0.0.1:8123, user `default`)
moon run cmd/main
# Point it at any ClickHouse via environment variables
CLICKHOUSE_HOST=127.0.0.1 CLICKHOUSE_PORT=38123 \
CLICKHOUSE_USER=default CLICKHOUSE_PASSWORD=barn \
moon run cmd/mainpub suberror DbError {
ServerError(code~ : Int, name~ : String, message~ : String)
ConnectionError(String)
} derive(Debug)pub struct Column {
name : String
type_ : String
}pub struct Connection {
host : String
port : Int
user : String
password : String
database : String
client_name : String
timeout_ms : Int
https : Bool
skip_verify : Bool
compress : Bool
}async fn Connection::execute_query(self : Connection, sql : String, params? : Map[String, String]) -> ResultSetasync fn Connection::execute_stream(self : Connection, sql : String, params? : Map[String, String]) -> ResultSetCursorasync fn Connection::insert(self : Connection, table : String, columns : Array[String], rows : Array[Array[String]], format? : InsertFormat) -> Unitpub struct ConnectionPool {
// private fields
}async fn ConnectionPool::execute(self : ConnectionPool, sql : String, values : Array[String]) -> ResultSetasync fn ConnectionPool::execute_query(self : ConnectionPool, sql : String, params? : Map[String, String]) -> ResultSetasync fn ConnectionPool::execute_stream(self : ConnectionPool, sql : String, params? : Map[String, String]) -> ResultSetCursorasync fn ConnectionPool::insert(self : ConnectionPool, table : String, columns : Array[String], rows : Array[Array[String]], format? : InsertFormat) -> Unitpub(all) enum InsertFormat {
Tsv
Ndjson
}pub struct ResultSetCursor {
// private fields
}fn connect(host~ : String, port~ : Int, user~ : String, password~ : String, database~ : String, client_name~ : String, timeout_ms? : Int, https? : Bool, skip_verify? : Bool, compress? : Bool) -> Connection一个ClickHouse的驱动库
Dependencies