quill_sql/storage/
tuple.rs

1use crate::catalog::{Schema, SchemaRef, EMPTY_SCHEMA_REF};
2use crate::error::{QuillSQLError, QuillSQLResult};
3use crate::storage::page::{RecordId, TupleMeta};
4use crate::transaction::TransactionId;
5use crate::utils::scalar::ScalarValue;
6use crate::utils::table_ref::TableReference;
7use std::cmp::Ordering;
8use std::fmt::{Display, Formatter};
9use std::sync::{Arc, LazyLock};
10
11pub static EMPTY_TUPLE: LazyLock<Tuple> = LazyLock::new(|| Tuple::empty(EMPTY_SCHEMA_REF.clone()));
12
13#[derive(Debug, Clone, Eq, PartialEq)]
14pub struct Tuple {
15    pub schema: SchemaRef,
16    pub data: Vec<ScalarValue>,
17}
18
19impl Tuple {
20    pub fn new(schema: SchemaRef, data: Vec<ScalarValue>) -> Self {
21        debug_assert_eq!(schema.columns.len(), data.len());
22        Self { schema, data }
23    }
24
25    pub fn project_with_schema(&self, projected_schema: SchemaRef) -> QuillSQLResult<Self> {
26        let indices = projected_schema
27            .columns
28            .iter()
29            .map(|col| {
30                self.schema
31                    .index_of(col.relation.as_ref(), col.name.as_str())
32            })
33            .collect::<QuillSQLResult<Vec<usize>>>()?;
34        let projected_data = indices
35            .iter()
36            .map(|idx| self.data[*idx].clone())
37            .collect::<Vec<ScalarValue>>();
38        Ok(Self::new(projected_schema, projected_data))
39    }
40
41    pub fn empty(schema: SchemaRef) -> Self {
42        let mut data = vec![];
43        for col in schema.columns.iter() {
44            data.push(ScalarValue::new_empty(col.data_type));
45        }
46        Self::new(schema, data)
47    }
48
49    pub fn is_visible(meta: &TupleMeta, txn_id: TransactionId) -> bool {
50        if meta.is_deleted {
51            return false;
52        }
53        meta.insert_txn_id <= txn_id
54    }
55
56    pub fn try_merge(tuples: impl IntoIterator<Item = Self>) -> QuillSQLResult<Self> {
57        let mut data = vec![];
58        let mut merged_schema = Schema::empty();
59        for tuple in tuples {
60            data.extend(tuple.data);
61            merged_schema = Schema::try_merge(vec![merged_schema, tuple.schema.as_ref().clone()])?;
62        }
63        Ok(Self::new(Arc::new(merged_schema), data))
64    }
65
66    pub fn is_null(&self) -> bool {
67        self.data.iter().all(|x| x.is_null())
68    }
69
70    pub fn visible_to(&self, meta: &TupleMeta, txn_id: TransactionId) -> bool {
71        if meta.is_deleted && meta.delete_txn_id <= txn_id {
72            return false;
73        }
74        meta.insert_txn_id <= txn_id
75    }
76
77    pub fn value(&self, index: usize) -> QuillSQLResult<&ScalarValue> {
78        self.data.get(index).ok_or(QuillSQLError::Internal(format!(
79            "Not found column data at {} in tuple: {:?}",
80            index, self
81        )))
82    }
83    pub fn value_by_name(
84        &self,
85        relation: Option<&TableReference>,
86        name: &str,
87    ) -> QuillSQLResult<&ScalarValue> {
88        let idx = self.schema.index_of(relation, name)?;
89        self.value(idx)
90    }
91
92    pub fn as_rid(&self) -> QuillSQLResult<RecordId> {
93        if self.data.len() < 2 {
94            return Err(QuillSQLError::Execution(
95                "RID tuple must have at least two columns".to_string(),
96            ));
97        }
98        let page_id = value_as_u32(&self.data[0])?;
99        let slot_num = value_as_u32(&self.data[1])?;
100        Ok(RecordId::new(page_id, slot_num))
101    }
102}
103
104impl Ord for Tuple {
105    fn cmp(&self, other: &Self) -> Ordering {
106        self.partial_cmp(other).unwrap_or(Ordering::Equal)
107    }
108}
109
110impl PartialOrd for Tuple {
111    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
112        let column_count = self.schema.column_count();
113        for idx in 0..column_count {
114            let order = self.value(idx).ok()?.partial_cmp(other.value(idx).ok()?)?;
115            if order == Ordering::Equal {
116                continue;
117            } else {
118                return Some(order);
119            }
120        }
121        Some(Ordering::Equal)
122    }
123}
124
125impl Display for Tuple {
126    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
127        let values = self
128            .data
129            .iter()
130            .map(|v| v.to_string())
131            .collect::<Vec<String>>()
132            .join(", ");
133        write!(f, "({})", values)
134    }
135}
136
137#[cfg(test)]
138mod tests {
139    use crate::catalog::{Column, DataType, Schema};
140    use std::cmp::Ordering;
141    use std::sync::Arc;
142
143    #[test]
144    pub fn tuple_compare() {
145        let schema = Arc::new(Schema::new(vec![
146            Column::new("a", DataType::Int8, false),
147            Column::new("b", DataType::Int16, false),
148        ]));
149        let tuple1 = super::Tuple::new(schema.clone(), vec![1i8.into(), 2i16.into()]);
150        let tuple2 = super::Tuple::new(schema.clone(), vec![1i8.into(), 2i16.into()]);
151        let tuple3 = super::Tuple::new(schema.clone(), vec![1i8.into(), 3i16.into()]);
152        let tuple4 = super::Tuple::new(schema.clone(), vec![2i8.into(), 2i16.into()]);
153        let tuple5 = super::Tuple::new(schema.clone(), vec![1i8.into(), 1i16.into()]);
154
155        assert_eq!(tuple1.partial_cmp(&tuple2).unwrap(), Ordering::Equal);
156        assert_eq!(tuple1.partial_cmp(&tuple3).unwrap(), Ordering::Less);
157        assert_eq!(tuple1.partial_cmp(&tuple4).unwrap(), Ordering::Less);
158        assert_eq!(tuple1.partial_cmp(&tuple5).unwrap(), Ordering::Greater);
159    }
160}
161
162fn value_as_u32(value: &ScalarValue) -> QuillSQLResult<u32> {
163    let num = match value {
164        ScalarValue::Int16(Some(v)) => *v as i32,
165        ScalarValue::Int32(Some(v)) => *v,
166        ScalarValue::Int64(Some(v)) => *v as i32,
167        ScalarValue::UInt16(Some(v)) => *v as i32,
168        ScalarValue::UInt32(Some(v)) => *v as i32,
169        ScalarValue::UInt64(Some(v)) => *v as i32,
170        ScalarValue::Int8(Some(v)) => *v as i32,
171        ScalarValue::UInt8(Some(v)) => *v as i32,
172        _ => {
173            return Err(QuillSQLError::Execution(
174                "RID column must be integer".to_string(),
175            ))
176        }
177    };
178    if num < 0 {
179        return Err(QuillSQLError::Execution(
180            "RID column must be positive".to_string(),
181        ));
182    }
183    Ok(num as u32)
184}