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