///|
/// 原生协议(Native Protocol, TCP 9000)演示示例。
///
/// 通过 `CLICKHOUSE_NATIVE=1` 切换(HTTP 版默认):
/// CLICKHOUSE_NATIVE=1 CLICKHOUSE_NATIVE_PORT=39000 moon run cmd/main
///
/// 展示:连接 + 握手、ping、SELECT 基础类型、NULL 值、错误处理、连接复用。
///|
async fn run_native_demo() -> Unit {
let host = @env.get_env_var("CLICKHOUSE_NATIVE_HOST").unwrap_or("127.0.0.1")
let port = match @env.get_env_var("CLICKHOUSE_NATIVE_PORT") {
Some(p) => parse_int_or(p, 9000)
None => 9000
}
let user = @env.get_env_var("CLICKHOUSE_NATIVE_USER").unwrap_or("default")
let password = @env.get_env_var("CLICKHOUSE_NATIVE_PASSWORD").unwrap_or(
"barn",
)
let database = @env.get_env_var("CLICKHOUSE_NATIVE_DATABASE").unwrap_or(
"default",
)
let conn = @native.connect(
@native.native_config(
host~,
port~,
user~,
password~,
database~,
client_name="moonbit-native-cli",
),
)
defer conn.close()
println(
"[0] native connected to " +
host +
":" +
port.to_string() +
" server=" +
conn.server_version() +
" tz=" +
conn.server_timezone(),
)
try {
// ---- 1. ping ----
conn.ping()
println("[1] native ping ok")
// ---- 2. SELECT 基础类型 ----
let rs = conn.execute_query(
"SELECT toUInt8(1) AS u8, toInt32(-42) AS i32, toFloat64(3.14) AS f64, " +
"'hello' AS s, toDate('2026-08-03') AS d, toDateTime('2026-08-03 12:34:56') AS dt",
)
println("[2] columns=" + rs.columns.length().to_string())
if rs.rows.length() > 0 {
println("[2] row=" + @debug.to_string(rs.rows[0].values))
}
// ---- 3. 多行 + NULL ----
let rs2 = conn.execute_query(
"SELECT number AS n, if(number % 2 = 0, toString(number), NULL) AS even FROM numbers(4)",
)
println(
"[3] rows=" +
rs2.rows.length().to_string() +
" (NULL 显示为空字符串)",
)
for row in rs2.rows {
println("[3] " + @debug.to_string(row.values))
}
// ---- 4. 语法错误 → ServerError(连接保持可用)----
let caught = try {
ignore(conn.execute_query("SLECT 1"))
false
} catch {
@lib.DbError::ServerError(code~, name=_, message~) => {
println(
"[4] expected ServerError code=" + code.to_string() + ": " + message,
)
true
}
_ => false
}
if caught {
conn.ping()
println("[4] connection reusable after error")
}
// ---- 5. INSERT 批量插入(Data 块上传)----
ignore(
conn.execute_query(
"CREATE TABLE IF NOT EXISTS native_demo_ins (id UInt64, name String, note Nullable(String)) ENGINE = Memory",
),
)
ignore(conn.execute_query("TRUNCATE TABLE native_demo_ins"))
conn.insert("native_demo_ins", ["id", "name", "note"], [
["1", "alice", "hello"],
["2", "bob", ""],
["3", "carol", "world"],
])
let rs_ins = conn.execute_query(
"SELECT id, name, note FROM native_demo_ins ORDER BY id",
)
println("[5] insert 3 rows, select back:")
for row in rs_ins.rows {
println("[5] " + @debug.to_string(row.values))
}
ignore(conn.execute_query("DROP TABLE IF EXISTS native_demo_ins"))
// ---- 6. 命名参数 + 位置参数绑定 ----
let rs_p1 = conn.execute_query(
"SELECT {n: UInt32} AS n, {s: String} AS s",
params=Map::from_array([("n", "42"), ("s", "hello")]),
)
let rs_p2 = conn.execute("SELECT ? AS a, ? AS b", ["7", "world"])
println(
"[6] named=" +
@debug.to_string(rs_p1.rows[0].values) +
" positional=" +
@debug.to_string(rs_p2.rows[0].values),
)
// ---- 7. 流式游标(逐块读取,不缓存全部结果)----
let cur = conn.execute_stream("SELECT number AS n FROM numbers(5)")
println("[7] stream columns=" + cur.columns().length().to_string())
let mut n_rows = 0
while true {
match cur.next() {
Some(row) => {
n_rows = n_rows + 1
println("[7] row=" + @debug.to_string(row.values))
}
None => break
}
}
cur.close()
println("[7] stream rows=" + n_rows.to_string())
// ---- 8. 连接池(复用持久连接,acquire/release 模式)----
let pool = @native.native_config(host~, port~, user~, password~, database~).pool(
size=2,
)
ignore(
pool.execute_query(
"CREATE TABLE IF NOT EXISTS native_pool_demo (id UInt32, name String) ENGINE = Memory",
),
)
ignore(pool.execute_query("TRUNCATE TABLE native_pool_demo"))
let rs_pool = pool.execute_query("SELECT number AS n FROM numbers(4)")
println("[8] pool query rows=" + rs_pool.rows.length().to_string())
pool.insert("native_pool_demo", ["id", "name"], [["10", "pooled"]])
let rs_pool2 = pool.execute_query(
"SELECT name FROM native_pool_demo WHERE id = 10",
)
println("[8] pool insert-back=" + rs_pool2.rows[0].values[0])
ignore(pool.execute_query("DROP TABLE IF EXISTS native_pool_demo"))
pool.close()
// ---- 9. 无结果查询(仍返回列头)----
let rs3 = conn.execute_query(
"SELECT number AS n FROM numbers(3) WHERE number > 100",
)
println(
"[5] empty result columns=" +
rs3.columns.length().to_string() +
" rows=" +
rs3.rows.length().to_string(),
)
} catch {
@lib.DbError::ServerError(code~, name=_, message~) =>
println("TOP-LEVEL ServerError: code=" + code.to_string() + " " + message)
@lib.DbError::ConnectionError(msg) =>
println("TOP-LEVEL ConnectionError: " + msg)
_ as e => println("TOP-LEVEL unknown: " + e.to_string())
}
println("Native demo completed.")
}