Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,11 @@ jobs:
bash .github/scripts/run-in-rust-container.sh \
'bash .github/scripts/rust-container-setup.sh && cargo test --test '"'"'*'"'"''

- name: Run hyperbytedb-cli tests
run: |
bash .github/scripts/run-in-rust-container.sh \
'bash .github/scripts/rust-container-setup.sh && cargo test -p hyperbytedb-cli'

build:
name: Build
runs-on: hyperbytedb-operator-controller
Expand All @@ -116,7 +121,10 @@ jobs:

- uses: Swatinem/rust-cache@v2

- name: Build release binary
- name: Build release binaries
run: |
bash .github/scripts/run-in-rust-container.sh \
'bash .github/scripts/rust-container-setup.sh && cargo build --release'
'bash .github/scripts/rust-container-setup.sh && \
cargo build --release -p hyperbytedb --bin hyperbytedb -p hyperbytedb-cli --bin hyperbytedb-cli && \
test -x target/release/hyperbytedb && \
test -x target/release/hyperbytedb-cli'
6 changes: 5 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,12 @@ jobs:
set -euo pipefail
mkdir -p out
for arch in x86_64 aarch64; do
test -s "out/hyperbytedb-${TAG}-linux-${arch}.tar.gz"
tarball="out/hyperbytedb-${TAG}-linux-${arch}.tar.gz"
test -s "${tarball}"
test -s "out/hyperbytedb-${TAG}-linux-${arch}.tar.gz.sha256"
tar -tzf "${tarball}" | grep -qx hyperbytedb
tar -tzf "${tarball}" | grep -qx hyperbytedb-cli
tar -tzf "${tarball}" | grep -qx libchdb.so
done
ls -lh out/

Expand Down
6 changes: 3 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 8 additions & 6 deletions docs/developer-guide/building-and-ci.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ HyperbyteDB uses Cargo as its sole build system. There is no `build.rs`, `Makefi

| Name | Source | Description |
|------|--------|-------------|
| `hyperbytedb` | `src/main.rs` (implicit) | Main database server |
| `hyperbytedb` | `hyperbytedb/src/main.rs` (implicit) | Main database server |
| `hyperbytedb-cli` | `hyperbytedb-cli/src/main.rs` | Interactive CLI client (REPL, query, write, import) |

### Features

Expand All @@ -32,8 +33,9 @@ HyperbyteDB uses Cargo as its sole build system. There is no `build.rs`, `Makefi

```bash
cargo build # Debug build (all binaries)
cargo build --release # Release build
cargo build --release # Release build (all workspace binaries)
cargo build --release --bin hyperbytedb # Release build, main binary only
cargo build --release -p hyperbytedb-cli # CLI only
cargo build --no-default-features # Without columnar ingest
```

Expand All @@ -49,8 +51,8 @@ Triggers on `push` and `pull_request` to `main`. Uses concurrency groups with ca

1. **Format** — `cargo fmt --all --check`
2. **Clippy** (depends on Format) — `cargo clippy --all-targets -- -D warnings`
3. **Test** (depends on Format) — `cargo test --lib` + `cargo test --test '*'`
4. **Build** (depends on Clippy + Test) — `cargo build --release`
3. **Test** (depends on Format) — `cargo test --lib` + `cargo test --test '*'` + `cargo test -p hyperbytedb-cli`
4. **Build** (depends on Clippy + Test) — `cargo build --release` for `hyperbytedb` and `hyperbytedb-cli`

All jobs with Rust compilation install:
- System packages: `clang`, `llvm-dev`, `libclang-dev`, `pkg-config`, `libssl-dev`
Expand Down Expand Up @@ -88,7 +90,7 @@ Triggers on `push` to `main`, version tags (`v*`), and pull requests.
**Runtime stage** (Debian bookworm-slim):
1. Install runtime dependencies: `ca-certificates`, `libstdc++6`, `curl`.
2. Copy `libchdb.so` and `chdb.h` from builder.
3. Copy the `hyperbytedb` binary and `libchdb.so`.
3. Copy the `hyperbytedb` and `hyperbytedb-cli` binaries and `libchdb.so`.
4. Create data directories under `/var/lib/hyperbytedb/`.
5. Set environment defaults for data paths.
6. `ENTRYPOINT ["hyperbytedb"]`, `CMD ["serve"]`.
Expand All @@ -114,7 +116,7 @@ docker run -d -p 8086:8086 hyperbytedb:dev
2. Update `version` in `Cargo.toml`.
3. Tag the commit: `git tag vX.Y.Z` (must match `version` in `Cargo.toml`).
4. Push the tag: `git push origin vX.Y.Z`.
5. The container workflow automatically builds and pushes the Docker image with semver tags.
5. The release workflow (`release.yml`) builds and pushes the multi-arch Docker image, packages `hyperbytedb` + `hyperbytedb-cli` + `libchdb.so` tarballs per platform, and publishes a GitHub Release.

---

Expand Down
5 changes: 4 additions & 1 deletion docs/developer-guide/system-architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,7 @@ Columns mirror the measurement schema registered in metadata:
| `user:{username}` | `StoredUser` | Auth credentials |
| `tombstone:{db}:{meas}:{uuid}` | predicate + timestamp | DELETE tombstones |
| `cq:{db}:{name}` | `ContinuousQueryDef` | Continuous query definitions |
| `mv:{db}:{name}` | `MaterializedViewDef` | Materialized view definitions |

---

Expand Down Expand Up @@ -482,7 +483,7 @@ The parser examines the first keyword (case-insensitive) and dispatches:
|-------------|---------|
| `SELECT` | `parse_select()` |
| `SHOW` | `parse_show()` → further dispatch by second/third token |
| `CREATE` | `parse_create()` → `CREATE DATABASE`, `CREATE RETENTION POLICY`, `CREATE USER`, `CREATE CONTINUOUS QUERY` |
| `CREATE` | `parse_create()` → `CREATE DATABASE`, `CREATE RETENTION POLICY`, `CREATE USER`, `CREATE CONTINUOUS QUERY`, `CREATE MATERIALIZED VIEW` |
| `DROP` | `parse_drop()` → `DROP DATABASE`, `DROP MEASUREMENT`, etc. |
| `DELETE` | `parse_delete()` |
| `ALTER` | `parse_alter()` |
Expand Down Expand Up @@ -760,6 +761,8 @@ HyperbyteDB uses **master-master (peer-to-peer) replication** for data writes, w
| DROP USER | `/internal/replicate-mutation` | All peers |
| CREATE CONTINUOUS QUERY | `/internal/replicate-mutation` | All peers |
| DROP CONTINUOUS QUERY | `/internal/replicate-mutation` | All peers |
| CREATE MATERIALIZED VIEW | `/internal/replicate-mutation` | All peers (DDL reconciled on startup) |
| DROP MATERIALIZED VIEW | `/internal/replicate-mutation` | All peers |
| CREATE RETENTION POLICY | `/internal/replicate-mutation` | All peers |

### Replication protocol
Expand Down
1 change: 1 addition & 0 deletions docs/glossary.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Shared terminology used across HyperbyteDB documentation.
| **Column Family (CF)** | A RocksDB concept. HyperbyteDB uses separate column families for WAL entries (`wal`), WAL metadata (`wal_meta`), general metadata (`metadata`), and replication state. |
| **Composition Root** | `bootstrap.rs` — the single location where all adapters and services are wired together using dependency injection via `Arc<dyn Trait>`. |
| **Continuous Query (CQ)** | A named query that runs automatically on a schedule, typically used for downsampling raw data into summary measurements. |
| **Materialized View (MV)** | A TimeseriesQL downsampling definition backed by ClickHouse `MATERIALIZED VIEW` objects. Updates incrementally on each source flush. |
| **Drain** | Graceful node removal procedure: stop accepting writes, flush WAL, wait for replication acks, notify peers, shut down. |
| **Field** | A key-value pair in a data point that holds the actual measured value. Fields are not indexed. Field types are enforced after first write (Float, Integer, UInteger, String, Boolean). |
| **Figment** | Configuration loading library used by HyperbyteDB. Merges defaults, TOML config file, and environment variables. |
Expand Down
44 changes: 44 additions & 0 deletions docs/user-guide/advanced-features.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,50 @@ CQ definitions are stored in metadata and survive restarts. The background sched

---

## Materialized Views

Materialized views downsample data incrementally using ClickHouse `MATERIALIZED VIEW` objects. Each new flush to the source measurement triggers aggregation into the destination measurement — no background scheduler.

### Create a materialized view

```sql
CREATE MATERIALIZED VIEW "mv_cpu_1h" ON "mydb"
AS SELECT mean("usage_idle") INTO "cpu_1h" FROM "cpu" GROUP BY time(1h), *
```

Requirements (same as `SELECT INTO` / continuous queries):

- `INTO` destination measurement is required
- `GROUP BY time(<interval>)` is required
- Single concrete source measurement (no regex `FROM` in v1)

On `CREATE`, HyperbyteDB:

1. Registers the destination measurement in metadata
2. Creates destination MergeTree tables in chDB
3. Installs fact and series ClickHouse materialized views
4. Backfills historical data from the source

### Manage materialized views

```sql
SHOW MATERIALIZED VIEWS
DROP MATERIALIZED VIEW "mv_cpu_1h" ON "mydb"
```

`DROP MATERIALIZED VIEW` removes the ClickHouse MV objects and metadata. The destination measurement and its data are kept.

### Continuous queries vs materialized views

| | Continuous Query | Materialized View |
|--|------------------|-------------------|
| Trigger | 10s scheduler | Each flush to source |
| Latency | Up to resample interval | Near real-time |
| Backfill | Re-scans window each run | One-time on CREATE; then incremental |
| Engine | WAL writeback | ClickHouse MV |

---

### User authentication

Enable `[auth] enabled = true` to require credentials on `/write` and `/query`. How credentials are sent, which HTTP paths stay public, and when **admin** is required for cluster/internal APIs are all covered in **[Authentication](authentication.md)**.
Expand Down
5 changes: 3 additions & 2 deletions docs/user-guide/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Backup and restore remain **server-local** operations on the `hyperbytedb` binar
Each `v*` GitHub Release ships `hyperbytedb-cli` alongside `hyperbytedb` and `libchdb.so`:

```bash
tar -xzf hyperbytedb-v0.8.0-linux-x86_64.tar.gz
tar -xzf hyperbytedb-vx.x.x-linux-x86_64.tar.gz
./hyperbytedb-cli -host http://localhost:8086 ping
```

Expand Down Expand Up @@ -43,7 +43,7 @@ hyperbytedb-cli -host http://localhost:8086
```

```
Connected to http://localhost:8086 (HyperbyteDB-0.8.0)
Connected to http://localhost:8086 (HyperbyteDB-x.x.x)
hyperbytedb> CREATE DATABASE telemetry
hyperbytedb> USE telemetry
telemetry> SHOW MEASUREMENTS
Expand Down Expand Up @@ -180,6 +180,7 @@ All schema and admin operations use TimeseriesQL in the REPL or `-execute`:
- `SHOW MEASUREMENTS`, `SHOW TAG KEYS/VALUES`, `SHOW FIELD KEYS`, `SHOW SERIES`
- `SELECT`, `DELETE`, `DROP MEASUREMENT`
- `CREATE/DROP/SHOW CONTINUOUS QUERY`
- `CREATE/DROP/SHOW MATERIALIZED VIEW`

See [API & TimeseriesQL Reference](reference.md) for full syntax.

Expand Down
5 changes: 5 additions & 0 deletions docs/user-guide/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,11 @@ CREATE CONTINUOUS QUERY "<name>" ON "<db>"
[RESAMPLE EVERY <interval> FOR <interval>]
BEGIN <select_into_statement> END
DROP CONTINUOUS QUERY "<name>" ON "<db>"

CREATE MATERIALIZED VIEW "<name>" ON "<db>"
AS <select_into_statement>
DROP MATERIALIZED VIEW "<name>" ON "<db>"
SHOW MATERIALIZED VIEWS
```

### DELETE
Expand Down
2 changes: 1 addition & 1 deletion hyperbytedb-cli/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "hyperbytedb-cli"
version = "0.8.0"
version = "0.8.2"
edition = "2024"
description = "Interactive CLI client for HyperbyteDB — InfluxDB v1-compatible REPL, query, write, and admin"
license = "Apache-2.0"
Expand Down
68 changes: 65 additions & 3 deletions hyperbytedb-cli/src/client/query.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use serde::{Deserialize, Serialize};
use serde_json::Value;

use crate::error::{CliError, Result};
use crate::error::{CliError, Result, parse_json_error};
use crate::session::OutputFormat;

use super::HyperbytedbClient;
Expand Down Expand Up @@ -152,8 +152,10 @@ impl HyperbytedbClient {
});
}

serde_json::from_str(&body)
.map_err(|e| CliError::Query(format!("invalid JSON: {e}: {body}")))
serde_json::from_str(&body).map_err(|e| {
let detail = parse_json_error(&body);
CliError::Query(format!("invalid JSON ({e}): {detail}"))
})
}

pub async fn query_raw(&self, q: &str, opts: &QueryOptions) -> Result<String> {
Expand Down Expand Up @@ -198,4 +200,64 @@ impl QueryResponse {
pub fn first_error(&self) -> Option<&str> {
self.results.iter().find_map(|r| r.error.as_deref())
}

/// Format all per-statement errors into a single human-readable message.
pub fn format_errors(&self) -> String {
let errors: Vec<String> = self
.results
.iter()
.filter_map(|r| {
let err = r.error.as_deref()?;
if self.results.len() > 1 {
Some(format!("statement {}: {err}", r.statement_id))
} else {
Some(err.to_string())
}
})
.collect();
if errors.is_empty() {
"query failed".to_string()
} else {
errors.join("; ")
}
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn format_errors_single_statement() {
let resp = QueryResponse {
results: vec![StatementResult {
statement_id: 1,
series: None,
error: Some("syntax error".to_string()),
}],
};
assert_eq!(resp.format_errors(), "syntax error");
}

#[test]
fn format_errors_multiple_statements() {
let resp = QueryResponse {
results: vec![
StatementResult {
statement_id: 0,
series: None,
error: Some("first".to_string()),
},
StatementResult {
statement_id: 1,
series: None,
error: Some("second".to_string()),
},
],
};
assert_eq!(
resp.format_errors(),
"statement 0: first; statement 1: second"
);
}
}
Loading
Loading