quill_sql/storage/
tuple.rs1use 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}