dameng_rust_sdk 0.1.3

A Rust SDK for Dameng Database (DM8) with ODBC support
Documentation
//! Query builder for Dameng Rust SDK

use log::{debug};

use crate::error::{Error, Result as SdkResult};
use crate::types::{DamengValue};

/// Query builder for constructing SQL queries
pub struct QueryBuilder {
    schema: Option<String>,
    table: Option<String>,
    select_columns: Vec<String>,
    where_clause: Option<String>,
    order_by: Vec<String>,
    limit: Option<usize>,
    offset: Option<usize>,
    join_clauses: Vec<String>,
    group_by: Vec<String>,
    having_clause: Option<String>,
}

impl QueryBuilder {
    /// Create a new query builder
    pub fn new() -> Self {
        Self {
            schema: None,
            table: None,
            select_columns: Vec::new(),
            where_clause: None,
            order_by: Vec::new(),
            limit: None,
            offset: None,
            join_clauses: Vec::new(),
            group_by: Vec::new(),
            having_clause: None,
        }
    }

    /// Set the schema
    pub fn schema(mut self, schema: impl Into<String>) -> Self {
        self.schema = Some(schema.into());
        self
    }

    /// Set the table
    pub fn table(mut self, table: impl Into<String>) -> Self {
        self.table = Some(table.into());
        self
    }

    /// Add select columns
    pub fn select(mut self, columns: &[&str]) -> Self {
        self.select_columns.extend(columns.iter().map(|s| s.to_string()));
        self
    }

    /// Add a select column
    pub fn select_column(mut self, column: impl Into<String>) -> Self {
        self.select_columns.push(column.into());
        self
    }

    /// Set the where clause
    pub fn where_clause(mut self, clause: impl Into<String>) -> Self {
        self.where_clause = Some(clause.into());
        self
    }

    /// Add an order by clause
    pub fn order_by(mut self, column: impl Into<String>, ascending: bool) -> Self {
        let dir = if ascending { "ASC" } else { "DESC" };
        self.order_by.push(format!("{} {}", column.into(), dir));
        self
    }

    /// Set the limit
    pub fn limit(mut self, limit: usize) -> Self {
        self.limit = Some(limit);
        self
    }

    /// Set the offset
    pub fn offset(mut self, offset: usize) -> Self {
        self.offset = Some(offset);
        self
    }

    /// Add a join clause
    pub fn join(mut self, join_type: JoinType, table: impl Into<String>, on: impl Into<String>) -> Self {
        let type_str = match join_type {
            JoinType::Inner => "INNER JOIN",
            JoinType::Left => "LEFT JOIN",
            JoinType::Right => "RIGHT JOIN",
            JoinType::Full => "FULL JOIN",
        };
        self.join_clauses.push(format!("{} {} ON {}", type_str, table.into(), on.into()));
        self
    }

    /// Add group by columns
    pub fn group_by(mut self, columns: &[&str]) -> Self {
        self.group_by.extend(columns.iter().map(|s| s.to_string()));
        self
    }

    /// Set the having clause
    pub fn having(mut self, clause: impl Into<String>) -> Self {
        self.having_clause = Some(clause.into());
        self
    }

    /// Build the SQL query
    pub fn build(&self) -> SdkResult<String> {
        if self.table.is_none() {
            return Err(Error::query("Table name is required"));
        }

        let schema_prefix = self.schema.as_ref().map(|s| format!("{}.", s)).unwrap_or_default();
        let table_name = format!("{}{}", schema_prefix, self.table.as_ref().unwrap());

        let mut sql = String::new();

        // SELECT clause
        if self.select_columns.is_empty() {
            sql.push_str("SELECT *");
        } else {
            sql.push_str(&format!("SELECT {}", self.select_columns.join(", ")));
        }

        // FROM clause
        sql.push_str(&format!(" FROM {}", table_name));

        // JOIN clauses
        for join in &self.join_clauses {
            sql.push(' ');
            sql.push_str(join);
        }

        // WHERE clause
        if let Some(where_clause) = &self.where_clause {
            sql.push_str(&format!(" WHERE {}", where_clause));
        }

        // GROUP BY clause
        if !self.group_by.is_empty() {
            sql.push_str(&format!(" GROUP BY {}", self.group_by.join(", ")));
        }

        // HAVING clause
        if let Some(having) = &self.having_clause {
            sql.push_str(&format!(" HAVING {}", having));
        }

        // ORDER BY clause
        if !self.order_by.is_empty() {
            sql.push_str(&format!(" ORDER BY {}", self.order_by.join(", ")));
        }

        // LIMIT and OFFSET
        if let Some(limit) = self.limit {
            sql.push_str(&format!(" LIMIT {}", limit));
        }

        if let Some(offset) = self.offset {
            sql.push_str(&format!(" OFFSET {}", offset));
        }

        debug!("Built query: {}", sql);
        Ok(sql)
    }
}

impl Default for QueryBuilder {
    fn default() -> Self {
        Self::new()
    }
}

/// Join type for query builder
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum JoinType {
    /// Inner join
    Inner,
    /// Left join
    Left,
    /// Right join
    Right,
    /// Full join
    Full,
}

/// Batch insert builder
pub struct BatchInsertBuilder {
    table: Option<String>,
    schema: Option<String>,
    columns: Vec<String>,
    values: Vec<Vec<DamengValue>>,
}

impl BatchInsertBuilder {
    /// Create a new batch insert builder
    pub fn new() -> Self {
        Self {
            table: None,
            schema: None,
            columns: Vec::new(),
            values: Vec::new(),
        }
    }

    /// Set the table
    pub fn table(mut self, table: impl Into<String>) -> Self {
        self.table = Some(table.into());
        self
    }

    /// Set the schema
    pub fn schema(mut self, schema: impl Into<String>) -> Self {
        self.schema = Some(schema.into());
        self
    }

    /// Set the columns
    pub fn columns(mut self, columns: &[&str]) -> Self {
        self.columns = columns.iter().map(|s| s.to_string()).collect();
        self
    }

    /// Add a row of values
    pub fn add_row(mut self, values: Vec<DamengValue>) -> Self {
        self.values.push(values);
        self
    }

    /// Build the SQL query
    pub fn build(&self) -> SdkResult<String> {
        let table = self.table.as_ref().ok_or_else(|| Error::query("Table name is required"))?;
        let schema_prefix = self.schema.as_ref().map(|s| format!("{}.", s)).unwrap_or_default();

        if self.columns.is_empty() {
            return Err(Error::query("Columns are required"));
        }

        if self.values.is_empty() {
            return Err(Error::query("Values are required"));
        }

        let mut sql = String::new();
        sql.push_str(&format!("INSERT INTO {}{} (", schema_prefix, table));
        sql.push_str(&self.columns.join(", "));
        sql.push_str(") VALUES ");

        let placeholder_str = vec!["?"; self.columns.len()].join(", ");

        for (i, _row) in self.values.iter().enumerate() {
            if i > 0 {
                sql.push_str(", ");
            }
            sql.push_str(&format!("({})", placeholder_str));
        }

        debug!("Built batch insert query: {}", sql);
        Ok(sql)
    }

    /// Get the values
    pub fn values(&self) -> &[Vec<DamengValue>] {
        &self.values
    }
}

impl Default for BatchInsertBuilder {
    fn default() -> Self {
        Self::new()
    }
}