sift-query-sqlx 0.1.0

PostgreSQL renderer and sqlx executor for SiftQuery
Documentation
use sift_query_core::{ScalarValue, TypedList, ValueType};
use sqlx::Arguments;
use sqlx::postgres::PgArguments;

use crate::{PgBindValue, PgSearchError};

pub(crate) fn build_arguments(binds: &[PgBindValue]) -> Result<PgArguments, PgSearchError> {
    let mut arguments = PgArguments::default();
    macro_rules! add {
        ($value:expr) => {
            arguments
                .add($value)
                .map_err(|error| PgSearchError::Bind(error.to_string()))?
        };
    }

    for bind in binds {
        match bind {
            PgBindValue::Scalar(value) => match value {
                ScalarValue::Bool(value) => add!(*value),
                ScalarValue::I32(value) => add!(*value),
                ScalarValue::I64(value) => add!(*value),
                ScalarValue::F32(value) => add!(*value),
                ScalarValue::F64(value) => add!(*value),
                #[cfg(feature = "decimal")]
                ScalarValue::Decimal(value) => add!(*value),
                ScalarValue::Text(value) => add!(value.clone()),
                ScalarValue::Bytes(value) => add!(value.clone()),
                #[cfg(feature = "chrono")]
                ScalarValue::Date(value) => add!(*value),
                #[cfg(feature = "chrono")]
                ScalarValue::Time(value) => add!(*value),
                #[cfg(feature = "chrono")]
                ScalarValue::DateTime(value) => add!(*value),
                #[cfg(feature = "chrono")]
                ScalarValue::Timestamp(value) => add!(*value),
                #[cfg(feature = "uuid")]
                ScalarValue::Uuid(value) => add!(*value),
                #[cfg(feature = "json")]
                ScalarValue::Json(value) => add!(sqlx::types::Json(value.clone())),
                #[cfg(feature = "postgres-types")]
                ScalarValue::ArrayI64(value) => add!(value.clone()),
                #[cfg(feature = "postgres-types")]
                ScalarValue::ArrayText(value) => add!(value.clone()),
                #[cfg(feature = "postgres-types")]
                ScalarValue::Int8Range(value) => add!(crate::PgInt8Range(**value)),
                #[cfg(feature = "postgres-types")]
                ScalarValue::Network(value) => add!(**value),
                _ => {
                    return Err(PgSearchError::Bind(
                        "unsupported scalar binding type".to_owned(),
                    ));
                }
            },
            PgBindValue::Array(list) => match list.element_type {
                ValueType::Bool => add!(collect_array(list, |value| match value {
                    ScalarValue::Bool(value) => Some(*value),
                    _ => None,
                })?),
                ValueType::I32 => add!(collect_array(list, |value| match value {
                    ScalarValue::I32(value) => Some(*value),
                    _ => None,
                })?),
                ValueType::I64 => add!(collect_array(list, |value| match value {
                    ScalarValue::I64(value) => Some(*value),
                    _ => None,
                })?),
                ValueType::F32 => add!(collect_array(list, |value| match value {
                    ScalarValue::F32(value) => Some(*value),
                    _ => None,
                })?),
                ValueType::F64 => add!(collect_array(list, |value| match value {
                    ScalarValue::F64(value) => Some(*value),
                    _ => None,
                })?),
                #[cfg(feature = "decimal")]
                ValueType::Decimal => add!(collect_array(list, |value| match value {
                    ScalarValue::Decimal(value) => Some(*value),
                    _ => None,
                })?),
                ValueType::Text => add!(collect_array(list, |value| match value {
                    ScalarValue::Text(value) => Some(value.clone()),
                    _ => None,
                })?),
                #[cfg(feature = "chrono")]
                ValueType::Date => add!(collect_array(list, |value| match value {
                    ScalarValue::Date(value) => Some(*value),
                    _ => None,
                })?),
                #[cfg(feature = "chrono")]
                ValueType::Time => add!(collect_array(list, |value| match value {
                    ScalarValue::Time(value) => Some(*value),
                    _ => None,
                })?),
                #[cfg(feature = "chrono")]
                ValueType::DateTime => add!(collect_array(list, |value| match value {
                    ScalarValue::DateTime(value) => Some(*value),
                    _ => None,
                })?),
                #[cfg(feature = "chrono")]
                ValueType::Timestamp => add!(collect_array(list, |value| match value {
                    ScalarValue::Timestamp(value) => Some(*value),
                    _ => None,
                })?),
                #[cfg(feature = "uuid")]
                ValueType::Uuid => add!(collect_array(list, |value| match value {
                    ScalarValue::Uuid(value) => Some(*value),
                    _ => None,
                })?),
                ValueType::Bytes => {
                    return Err(PgSearchError::Bind(
                        "bytea array binding is not enabled in M1".to_owned(),
                    ));
                }
                #[cfg(feature = "json")]
                ValueType::Json => {
                    return Err(PgSearchError::Bind(
                        "JSON array binding is not enabled in M1".to_owned(),
                    ));
                }
                _ => {
                    return Err(PgSearchError::Bind(
                        "unsupported PostgreSQL array binding type".to_owned(),
                    ));
                }
            },
        }
    }
    Ok(arguments)
}

fn collect_array<T>(
    list: &TypedList,
    convert: impl Fn(&ScalarValue) -> Option<T>,
) -> Result<Vec<T>, PgSearchError> {
    list.values
        .iter()
        .map(|value| {
            convert(value)
                .ok_or_else(|| PgSearchError::Bind("heterogeneous array binding".to_owned()))
        })
        .collect()
}
#[cfg(test)]
mod tests;