hinge 0.1.0

SQL-native ELT engine — dependency graph resolved automatically from FROM/JOIN clauses, parallel execution, single binary
Documentation
use clickhouse::Client;

/// Executes SQL assets against a ClickHouse server.
///
/// For `table` assets, uses `EXCHANGE TABLES` to atomically swap the rebuilt
/// table with the live one — zero query downtime during a refresh.
///
/// # Connection URL
///
/// ```text
/// http://host:8123          plain HTTP (default ClickHouse port)
/// https://host:8443         HTTPS
/// clickhouse://host:9000    native protocol
/// ```
///
/// # Builder
///
/// ```rust
/// # use hinge::ClickHouseExecutor;
/// let executor = ClickHouseExecutor::new("http://localhost:8123")
///     .with_database("analytics")
///     .with_user("default")
///     .with_password("secret");
/// ```
pub struct ClickHouseExecutor {
    client: Client,
    default_database: Option<String>,
    require_order_by: bool,
}

impl ClickHouseExecutor {
    /// Create an executor pointing at `url`. No connection is made until the first [`Executor::run`] call.
    ///
    /// [`Executor::run`]: crate::Executor::run
    pub fn new(url: &str) -> Self {
        Self::from_client(Client::default().with_url(url))
    }

    pub fn from_client(client: Client) -> Self {
        Self {
            client,
            default_database: None,
            require_order_by: true,
        }
    }

    pub fn with_database(mut self, database: &str) -> Self {
        self.client = self.client.with_database(database);
        self.default_database = Some(database.to_string());
        self
    }

    pub fn with_user(mut self, user: &str) -> Self {
        self.client = self.client.with_user(user);
        self
    }

    pub fn with_password(mut self, password: &str) -> Self {
        self.client = self.client.with_password(password);
        self
    }

    /// Allow creating ClickHouse tables without an `ORDER BY` clause.
    /// By default, Hone requires it to prevent accidental full-scan tables.
    pub fn allow_unordered_tables(mut self) -> Self {
        self.require_order_by = false;
        self
    }

    pub(super) fn client(&self) -> &Client {
        &self.client
    }

    pub(super) fn default_database(&self) -> Option<&str> {
        self.default_database.as_deref()
    }

    pub(super) fn require_order_by(&self) -> bool {
        self.require_order_by
    }
}