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
53 changes: 47 additions & 6 deletions src/arg.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,44 @@
//! Query argument definitions for chDB.
//!
//! This module provides types for specifying query arguments such as output format,
//! log level, and custom command-line arguments.

use std::borrow::Cow;
use std::ffi::CString;

use crate::error::Error;
use crate::format::OutputFormat;
use crate::log_level::LogLevel;

/// Query arguments that can be passed when executing queries.
///
/// `Arg` represents various command-line arguments that can be used to configure
/// query execution. Most commonly, you'll use `OutputFormat` to specify the
/// desired output format.
///
/// # Examples
///
/// ```no_run
/// use chdb_rust::arg::Arg;
/// use chdb_rust::format::OutputFormat;
/// use chdb_rust::log_level::LogLevel;
///
/// // Specify output format
/// let args = &[Arg::OutputFormat(OutputFormat::JSONEachRow)];
///
/// // Specify log level
/// let args = &[Arg::LogLevel(LogLevel::Debug)];
///
/// // Use custom arguments
/// let args = &[Arg::Custom("path".into(), Some("/tmp/db".into()))];
/// ```
#[derive(Debug)]
pub enum Arg<'a> {
/// --config-file=<value>
/// `--config-file=<value>`
ConfigFilePath(Cow<'a, str>),
/// --log-level=<value>
/// `--log-level=<value>`
LogLevel(LogLevel),
/// --output-format=<value>
/// `--output-format=<value>`
OutputFormat(OutputFormat),
/// --multiquery
MultiQuery,
Expand All @@ -26,7 +53,7 @@ pub enum Arg<'a> {
/// 2. Arg::Custom("multiline".into(), None).
///
/// We should tell user where to look for officially supported arguments.
/// Here is some hint for now: https://github.com/fixcik/chdb-rs/blob/master/OPTIONS.md .
/// Here is some hint for now: <https://github.com/fixcik/chdb-rs/blob/master/OPTIONS.md>.
Custom(Cow<'a, str>, Option<Cow<'a, str>>),
}

Expand All @@ -45,7 +72,10 @@ impl<'a> Arg<'a> {
}?)
}

/// Extract OutputFormat from an Arg if it is an OutputFormat variant.
/// Extract `OutputFormat` from an `Arg` if it is an `OutputFormat` variant.
///
/// This is a helper method used internally to extract output format information
/// from query arguments.
pub(crate) fn as_output_format(&self) -> Option<OutputFormat> {
match self {
Self::OutputFormat(f) => Some(*f),
Expand All @@ -54,7 +84,18 @@ impl<'a> Arg<'a> {
}
}

/// Extract OutputFormat from a slice of Args, returns the first one found or default.
/// Extract `OutputFormat` from a slice of `Arg`s.
///
/// This function searches through the provided arguments and returns the first
/// `OutputFormat` found, or the default `TabSeparated` format if none is found.
///
/// # Arguments
///
/// * `args` - Optional slice of query arguments
///
/// # Returns
///
/// Returns the first `OutputFormat` found, or `OutputFormat::TabSeparated` as default.
pub(crate) fn extract_output_format(args: Option<&[Arg]>) -> OutputFormat {
args.and_then(|args| args.iter().find_map(|a| a.as_output_format()))
.unwrap_or(OutputFormat::TabSeparated)
Expand Down
128 changes: 125 additions & 3 deletions src/connection.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,75 @@
//! Connection management for chDB.
//!
//! This module provides the [`Connection`] type for managing connections to chDB databases.

use std::ffi::{c_char, CString};

use crate::bindings;
use crate::error::{Error, Result};
use crate::format::OutputFormat;
use crate::query_result::QueryResult;

/// A connection to chDB database.
/// A connection to a chDB database.
///
/// A `Connection` represents an active connection to a chDB database instance.
/// Connections can be created for in-memory databases or persistent databases
/// stored on disk.
///
/// # Thread Safety
///
/// `Connection` implements `Send`, meaning it can be safely transferred between threads.
/// However, the underlying chDB library may have limitations on concurrent access.
/// It's recommended to use one connection per thread or implement proper synchronization.
///
/// # Examples
///
/// ```no_run
/// use chdb_rust::connection::Connection;
/// use chdb_rust::format::OutputFormat;
///
/// // Create an in-memory connection
/// let conn = Connection::open_in_memory()?;
///
/// // Execute a query
/// let result = conn.query("SELECT 1", OutputFormat::JSONEachRow)?;
/// println!("{}", result.data_utf8_lossy());
/// # Ok::<(), chdb_rust::error::Error>(())
/// ```
#[derive(Debug)]
pub struct Connection {
// Pointer to chdb_connection (which is *mut chdb_connection_)
inner: *mut bindings::chdb_connection,
}

// Safety: Connection is safe to send between threads
// The underlying chDB library is thread-safe for query execution
unsafe impl Send for Connection {}

impl Connection {
/// Connect to chDB with the given arguments.
/// Use `--path=<db_path>` to specify database location, default is `:memory:`.
/// Connect to chDB with the given command-line arguments.
///
/// This is a low-level function that allows you to pass arbitrary arguments
/// to the chDB connection. For most use cases, prefer [`open_in_memory`](Self::open_in_memory)
/// or [`open_with_path`](Self::open_with_path).
///
/// # Arguments
///
/// * `args` - Array of command-line arguments (e.g., `["clickhouse", "--path=/tmp/db"]`)
///
/// # Examples
///
/// ```no_run
/// use chdb_rust::connection::Connection;
///
/// // Connect with custom arguments
/// let conn = Connection::open(&["clickhouse", "--path=/tmp/mydb"])?;
/// # Ok::<(), chdb_rust::error::Error>(())
/// ```
///
/// # Errors
///
/// Returns [`Error::ConnectionFailed`] if the
/// connection cannot be established.
pub fn open(args: &[&str]) -> Result<Self> {
let c_args: Vec<CString> = args
.iter()
Expand All @@ -42,17 +94,87 @@ impl Connection {
}

/// Connect to an in-memory database.
///
/// Creates a connection to a temporary in-memory database. Data stored in this
/// database will be lost when the connection is closed.
///
/// # Examples
///
/// ```no_run
/// use chdb_rust::connection::Connection;
///
/// let conn = Connection::open_in_memory()?;
/// # Ok::<(), chdb_rust::error::Error>(())
/// ```
///
/// # Errors
///
/// Returns [`Error::ConnectionFailed`] if the
/// connection cannot be established.
pub fn open_in_memory() -> Result<Self> {
Self::open(&["clickhouse"])
}

/// Connect to a database at the given path.
///
/// Creates a connection to a persistent database stored at the specified path.
/// The directory will be created if it doesn't exist.
///
/// # Arguments
///
/// * `path` - The filesystem path where the database should be stored
///
/// # Examples
///
/// ```no_run
/// use chdb_rust::connection::Connection;
///
/// let conn = Connection::open_with_path("/tmp/mydb")?;
/// # Ok::<(), chdb_rust::error::Error>(())
/// ```
///
/// # Errors
///
/// Returns [`Error::ConnectionFailed`] if the
/// connection cannot be established.
pub fn open_with_path(path: &str) -> Result<Self> {
let path_arg = format!("--path={}", path);
Self::open(&["clickhouse", &path_arg])
}

/// Execute a query and return the result.
///
/// Executes a SQL query against the database and returns the result in the
/// specified output format.
///
/// # Arguments
///
/// * `sql` - The SQL query string to execute
/// * `format` - The desired output format for the result
///
/// # Returns
///
/// Returns a [`QueryResult`] containing the query output, or an [`Error`]
/// if the query fails.
///
/// # Examples
///
/// ```no_run
/// use chdb_rust::connection::Connection;
/// use chdb_rust::format::OutputFormat;
///
/// let conn = Connection::open_in_memory()?;
/// let result = conn.query("SELECT 1 + 1 AS sum", OutputFormat::JSONEachRow)?;
/// println!("{}", result.data_utf8_lossy());
/// # Ok::<(), chdb_rust::error::Error>(())
/// ```
///
/// # Errors
///
/// Returns an error if:
/// - The query syntax is invalid
/// - The query references non-existent tables or columns
/// - The query execution fails for any other reason
pub fn query(&self, sql: &str, format: OutputFormat) -> Result<QueryResult> {
let query_cstr = CString::new(sql)?;
let format_cstr = CString::new(format.as_str())?;
Expand Down
25 changes: 25 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -1,28 +1,53 @@
//! Error types for chdb-rust.
//!
//! This module defines the error types used throughout the crate.

use std::ffi::NulError;
use std::string::FromUtf8Error;

/// Errors that can occur when using chdb-rust.
///
/// This enum represents all possible errors that can be returned by the library.
/// Most errors are self-explanatory, with `QueryError` containing the actual error
/// message from the underlying chDB library.
#[derive(Debug, thiserror::Error)]
pub enum Error {
/// An unknown error has occurred.
#[error("An unknown error has occurred")]
Unknown,
/// No result was returned from the query.
#[error("No result")]
NoResult,
/// Failed to establish a connection to chDB.
#[error("Connection failed")]
ConnectionFailed,
/// Invalid data was encountered.
#[error("Invalid data: {0}")]
InvalidData(String),
/// Invalid path was provided.
#[error("Invalid path")]
PathError,
/// An I/O error occurred.
#[error(transparent)]
Io(#[from] std::io::Error),
/// A null byte was found in a string where it's not allowed.
#[error(transparent)]
Nul(#[from] NulError),
/// Insufficient permissions to access the directory.
#[error("Insufficient dir permissions")]
InsufficientPermissions,
/// The data contains invalid UTF-8 sequences.
#[error("Non UTF-8 sequence: {0}")]
NonUtf8Sequence(FromUtf8Error),
/// A query execution error occurred.
///
/// This contains the error message from the underlying chDB library,
/// which typically includes details about SQL syntax errors, missing tables, etc.
#[error("{0}")]
QueryError(String),
}

/// A type alias for `Result<T, Error>`.
///
/// This is the standard result type used throughout the crate.
pub type Result<T, Err = Error> = std::result::Result<T, Err>;
Loading