bustubx 0.3.0

A simple relational database written in Rust
Documentation
use crate::catalog::{SchemaRef, EMPTY_SCHEMA_REF};
use crate::common::TableReference;
use crate::{catalog::Schema, common::ScalarValue, BustubxError, BustubxResult};
use std::cmp::Ordering;
use std::sync::Arc;

lazy_static::lazy_static! {
    pub static ref EMPTY_TUPLE: Tuple = Tuple::empty(EMPTY_SCHEMA_REF.clone());
}

#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Tuple {
    pub schema: SchemaRef,
    pub data: Vec<ScalarValue>,
}

impl Tuple {
    pub fn new(schema: SchemaRef, data: Vec<ScalarValue>) -> Self {
        debug_assert_eq!(schema.columns.len(), data.len());
        debug_assert!(schema
            .columns
            .iter()
            .zip(data.iter())
            .find(|(col, val)| ScalarValue::new_empty(col.data_type).data_type() != val.data_type())
            .is_none());
        Self { schema, data }
    }

    pub fn project_with_schema(&self, projected_schema: SchemaRef) -> BustubxResult<Self> {
        let indices = projected_schema
            .columns
            .iter()
            .map(|col| {
                self.schema
                    .index_of(col.relation.as_ref(), col.name.as_str())
            })
            .collect::<BustubxResult<Vec<usize>>>()?;
        let projected_data = indices
            .iter()
            .map(|idx| self.data[*idx].clone())
            .collect::<Vec<ScalarValue>>();
        Ok(Self::new(projected_schema, projected_data))
    }

    pub fn empty(schema: SchemaRef) -> Self {
        let mut data = vec![];
        for col in schema.columns.iter() {
            data.push(ScalarValue::new_empty(col.data_type));
        }
        Self::new(schema, data)
    }

    pub fn try_merge(tuples: impl IntoIterator<Item = Self>) -> BustubxResult<Self> {
        let mut data = vec![];
        let mut merged_schema = Schema::empty();
        for tuple in tuples {
            data.extend(tuple.data);
            merged_schema = Schema::try_merge(vec![merged_schema, tuple.schema.as_ref().clone()])?;
        }
        Ok(Self::new(Arc::new(merged_schema), data))
    }

    pub fn is_null(&self) -> bool {
        self.data.iter().all(|x| x.is_null())
    }

    pub fn value(&self, index: usize) -> BustubxResult<&ScalarValue> {
        self.data.get(index).ok_or(BustubxError::Internal(format!(
            "Not found column data at {} in tuple: {:?}",
            index, self
        )))
    }
    pub fn value_by_name(
        &self,
        relation: Option<&TableReference>,
        name: &str,
    ) -> BustubxResult<&ScalarValue> {
        let idx = self.schema.index_of(relation, name)?;
        self.value(idx)
    }
}

impl PartialOrd for Tuple {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        let column_count = self.schema.column_count();
        for idx in 0..column_count {
            let order = self.value(idx).ok()?.partial_cmp(other.value(idx).ok()?)?;
            if order == Ordering::Equal {
                continue;
            } else {
                return Some(order);
            }
        }
        Some(Ordering::Equal)
    }
}

#[cfg(test)]
mod tests {
    use crate::catalog::{Column, DataType, Schema};
    use std::cmp::Ordering;
    use std::sync::Arc;

    #[test]
    pub fn tuple_compare() {
        let schema = Arc::new(Schema::new(vec![
            Column::new("a", DataType::Int8, false),
            Column::new("b", DataType::Int16, false),
        ]));
        let tuple1 = super::Tuple::new(schema.clone(), vec![1i8.into(), 2i16.into()]);
        let tuple2 = super::Tuple::new(schema.clone(), vec![1i8.into(), 2i16.into()]);
        let tuple3 = super::Tuple::new(schema.clone(), vec![1i8.into(), 3i16.into()]);
        let tuple4 = super::Tuple::new(schema.clone(), vec![2i8.into(), 2i16.into()]);
        let tuple5 = super::Tuple::new(schema.clone(), vec![1i8.into(), 1i16.into()]);

        assert_eq!(tuple1.partial_cmp(&tuple2).unwrap(), Ordering::Equal);
        assert_eq!(tuple1.partial_cmp(&tuple3).unwrap(), Ordering::Less);
        assert_eq!(tuple1.partial_cmp(&tuple4).unwrap(), Ordering::Less);
        assert_eq!(tuple1.partial_cmp(&tuple5).unwrap(), Ordering::Greater);
    }
}