mirror of
https://github.com/hanzo-ds/rs.git
synced 2026-10-30 20:49:56 +00:00
Cut from the upstream v0.13.3 release tag (not HEAD) so this is a pure
rename with no behaviour change. v0.13.3 is what our consumers already
resolve to: insights/rust's Cargo.lock pins clickhouse 0.13.3 and its
manifest already declares `datastore` at 0.13.x.
The load-bearing change is the HTTP auth header rename. The Datastore
server reads only X-Datastore-User / X-Datastore-Key; the upstream names
return `401 Code: 194 Authentication failed`. Every un-renamed HTTP
client is therefore unable to authenticate at all — this is a
correctness fix, not branding.
- crate `clickhouse` -> `datastore`, `clickhouse-derive` -> `datastore-derive`;
versions preserved exactly (0.13.3 / 0.2.0).
- derive macro emits `datastore::Row` in lockstep, so downstream
`#[derive(Row)]` keeps resolving.
- User-Agent reports `datastore-rs/{version}`.
- test-harness env vars CLICKHOUSE_* -> DATASTORE_*; compose uses
ghcr.io/hanzoai/datastore with /etc/datastore-server config paths.
- doc links point at docs.hanzo.ai/datastore.
- `publish = false`: the `datastore` name on crates.io belongs to an
unrelated crate, so consume this by git tag, never by version range.
CHANGELOG is truncated to a fork notice rather than find-replaced —
rewriting upstream release history would manufacture changes Datastore
never shipped. References to upstream issues keep their numbers.
79 lines
2.5 KiB
Rust
79 lines
2.5 KiB
Rust
use datastore::{query::BytesCursor, Client};
|
|
use std::time::Instant;
|
|
use tokio::{fs::File, io::AsyncWriteExt};
|
|
|
|
// Examples of streaming the result of a query in an arbitrary format into a
|
|
// file. In this case, `CSVWithNamesAndTypes` format is used.
|
|
// Check also other formats in https://docs.hanzo.ai/datastore/interfaces/formats.
|
|
//
|
|
// Note: there is no need to wrap `File` into `BufWriter` because `BytesCursor`
|
|
// is buffered internally already and produces chunks of data.
|
|
|
|
const NUMBERS: u32 = 100_000;
|
|
|
|
fn query(numbers: u32) -> BytesCursor {
|
|
let client = Client::default().with_url("http://localhost:8123");
|
|
|
|
client
|
|
.query(
|
|
"SELECT number, hex(randomPrintableASCII(20)) AS hex_str
|
|
FROM system.numbers
|
|
LIMIT {limit: Int32}",
|
|
)
|
|
.param("limit", numbers)
|
|
.fetch_bytes("CSVWithNamesAndTypes")
|
|
.unwrap()
|
|
}
|
|
|
|
// Pattern 1: use the `tokio::io::copy_buf` helper.
|
|
//
|
|
// It shows integration with `tokio::io::AsyncBufWriteExt` trait.
|
|
async fn tokio_copy_buf(filename: &str) {
|
|
let mut cursor = query(NUMBERS);
|
|
let mut file = File::create(filename).await.unwrap();
|
|
tokio::io::copy_buf(&mut cursor, &mut file).await.unwrap();
|
|
}
|
|
|
|
// Pattern 2: use `BytesCursor::next()`.
|
|
async fn cursor_next(filename: &str) {
|
|
let mut cursor = query(NUMBERS);
|
|
let mut file = File::create(filename).await.unwrap();
|
|
|
|
while let Some(bytes) = cursor.next().await.unwrap() {
|
|
file.write_all(&bytes).await.unwrap();
|
|
println!("chunk of {}B written to {filename}", bytes.len());
|
|
}
|
|
}
|
|
|
|
// Pattern 3: use the `futures::(Try)StreamExt` traits.
|
|
#[cfg(feature = "futures03")]
|
|
async fn futures03_stream(filename: &str) {
|
|
use futures::TryStreamExt;
|
|
|
|
let mut cursor = query(NUMBERS);
|
|
let mut file = File::create(filename).await.unwrap();
|
|
|
|
while let Some(bytes) = cursor.try_next().await.unwrap() {
|
|
file.write_all(&bytes).await.unwrap();
|
|
println!("chunk of {}B written to {filename}", bytes.len());
|
|
}
|
|
}
|
|
|
|
#[tokio::main]
|
|
async fn main() {
|
|
let start = Instant::now();
|
|
tokio_copy_buf("output-1.csv").await;
|
|
println!("written to output-1.csv in {:?}", start.elapsed());
|
|
|
|
let start = Instant::now();
|
|
cursor_next("output-2.csv").await;
|
|
println!("written to output-2.csv in {:?}", start.elapsed());
|
|
|
|
#[cfg(feature = "futures03")]
|
|
{
|
|
let start = Instant::now();
|
|
futures03_stream("output-3.csv").await;
|
|
println!("written to output-3.csv in {:?}", start.elapsed());
|
|
}
|
|
}
|