tank-duckdb 0.9.0

DuckDB driver implementation for Tank: the Rust data layer
Documentation
use crate::{
    cbox::CBox, date_to_duckdb_date, decimal_to_duckdb_decimal, error_message_from_ptr,
    i128_to_duckdb_hugeint, interval_to_duckdb_interval, offsetdatetime_to_duckdb_timestamp,
    primitive_date_time_to_duckdb_timestamp, time_to_duckdb_time, u128_to_duckdb_uhugeint,
};
use libduckdb_sys::*;
use std::{
    ffi::c_void,
    fmt::{self, Display},
};
use tank_core::{AsValue, Error, Prepared, Result, Value};

pub struct DuckDBPrepared {
    pub(crate) statement: CBox<duckdb_prepared_statement>,
    pub(crate) index: u64,
}
impl DuckDBPrepared {
    pub(crate) fn new(statement: CBox<duckdb_prepared_statement>) -> Self {
        unsafe {
            duckdb_clear_bindings(*statement);
        }
        Self {
            statement,
            index: 1,
        }
    }
}

impl Display for DuckDBPrepared {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{:p}", *self.statement)
    }
}

impl Prepared for DuckDBPrepared {
    fn bind<V: AsValue>(&mut self, value: V) -> Result<&mut Self> {
        self.bind_index(value, self.index)
    }
    fn bind_index<V: AsValue>(&mut self, v: V, index: u64) -> Result<&mut Self> {
        unsafe {
            let prepared = *self.statement;
            let value = v.as_value();
            let state = match value {
                Value::Null
                | Value::Boolean(None, ..)
                | Value::Int8(None, ..)
                | Value::Int16(None, ..)
                | Value::Int32(None, ..)
                | Value::Int64(None, ..)
                | Value::Int128(None, ..)
                | Value::UInt8(None, ..)
                | Value::UInt16(None, ..)
                | Value::UInt32(None, ..)
                | Value::UInt64(None, ..)
                | Value::UInt128(None, ..)
                | Value::Float32(None, ..)
                | Value::Float64(None, ..)
                | Value::Decimal(None, ..)
                | Value::Char(None, ..)
                | Value::Varchar(None, ..)
                | Value::Blob(None, ..)
                | Value::Date(None, ..)
                | Value::Time(None, ..)
                | Value::Timestamp(None, ..)
                | Value::TimestampWithTimezone(None, ..)
                | Value::Interval(None, ..)
                | Value::Uuid(None, ..)
                | Value::Array(None, ..)
                | Value::List(None, ..)
                | Value::Map(None, ..)
                | Value::Struct(None, ..) => duckdb_bind_null(prepared, index),
                Value::Boolean(Some(v), ..) => duckdb_bind_boolean(prepared, index, v),
                Value::Int8(Some(v), ..) => duckdb_bind_int8(prepared, index, v),
                Value::Int16(Some(v), ..) => duckdb_bind_int16(prepared, index, v),
                Value::Int32(Some(v), ..) => duckdb_bind_int32(prepared, index, v),
                Value::Int64(Some(v), ..) => duckdb_bind_int64(prepared, index, v),
                Value::Int128(Some(v), ..) => {
                    duckdb_bind_hugeint(prepared, index, i128_to_duckdb_hugeint(v))
                }
                Value::UInt8(Some(v), ..) => duckdb_bind_uint8(prepared, index, v),
                Value::UInt16(Some(v), ..) => duckdb_bind_uint16(prepared, index, v),
                Value::UInt32(Some(v), ..) => duckdb_bind_uint32(prepared, index, v),
                Value::UInt64(Some(v), ..) => duckdb_bind_uint64(prepared, index, v),
                Value::UInt128(Some(v), ..) => {
                    duckdb_bind_uhugeint(prepared, index, u128_to_duckdb_uhugeint(v))
                }
                Value::Float32(Some(v), ..) => duckdb_bind_float(prepared, index, v),
                Value::Float64(Some(v), ..) => duckdb_bind_double(prepared, index, v),
                Value::Decimal(Some(v), w, s) => {
                    duckdb_bind_decimal(prepared, index, decimal_to_duckdb_decimal(&v, w, s))
                }
                Value::Char(Some(v), ..) => {
                    let v = v.to_string();
                    let status =
                        duckdb_bind_varchar_length(prepared, index, v.as_ptr() as *const i8, 1);
                    status
                }
                Value::Varchar(Some(v), ..) => {
                    let status = duckdb_bind_varchar_length(
                        prepared,
                        index,
                        v.as_ptr() as *const i8,
                        v.len() as u64,
                    );
                    status
                }
                Value::Blob(Some(v), ..) => {
                    let status = duckdb_bind_blob(
                        prepared,
                        index,
                        v.as_ptr() as *const c_void,
                        v.len() as u64,
                    );
                    status
                }
                Value::Date(Some(v), ..) => {
                    duckdb_bind_date(prepared, index, date_to_duckdb_date(&v))
                }
                Value::Time(Some(v), ..) => {
                    duckdb_bind_time(prepared, index, time_to_duckdb_time(&v))
                }
                Value::Timestamp(Some(v), ..) => duckdb_bind_timestamp(
                    prepared,
                    index,
                    primitive_date_time_to_duckdb_timestamp(&v),
                ),
                Value::TimestampWithTimezone(Some(v), ..) => duckdb_bind_timestamp_tz(
                    prepared,
                    index,
                    offsetdatetime_to_duckdb_timestamp(&v),
                ),
                Value::Interval(Some(v), ..) => {
                    duckdb_bind_interval(prepared, index, interval_to_duckdb_interval(&v))
                }
                Value::Uuid(Some(v), ..) => {
                    let v = CBox::new(
                        duckdb_create_uuid(u128_to_duckdb_uhugeint(v.as_u128())),
                        |mut ptr| duckdb_destroy_value(&mut ptr),
                    );
                    duckdb_bind_value(prepared, index, *v)
                }
                _ => {
                    let error =
                        Error::msg(format!("Cannot use a {:?} as a query parameter", value));
                    log::error!("{:#}", error);
                    return Err(error);
                }
            };
            if state != duckdb_state_DuckDBSuccess {
                let error =
                    Error::msg(error_message_from_ptr(&duckdb_prepare_error(prepared)).to_string())
                        .context(format!("While trying to bind the parameter {}", index));
                log::error!("{:#}", error);
                return Err(error);
            }
            self.index = index + 1;
            Ok(self)
        }
    }
}

impl From<CBox<duckdb_prepared_statement>> for DuckDBPrepared {
    fn from(value: CBox<duckdb_prepared_statement>) -> Self {
        Self::new(value)
    }
}