1use rudb_common::{Error, LogicalType, Result, Value};
15use rudb_vector::vector::VECTOR_SIZE;
16use rudb_vector::{Chunk, Vector};
17
18#[derive(Debug, Clone)]
20pub struct MemoryTable {
21 types: Vec<LogicalType>,
22 chunks: Vec<Chunk>,
23 rows: usize,
24}
25
26impl MemoryTable {
27 #[must_use]
29 pub fn new(types: Vec<LogicalType>) -> Self {
30 Self { types, chunks: Vec::new(), rows: 0 }
31 }
32
33 #[must_use]
35 pub fn types(&self) -> &[LogicalType] {
36 &self.types
37 }
38
39 #[must_use]
41 pub fn width(&self) -> usize {
42 self.types.len()
43 }
44
45 #[must_use]
47 pub fn len(&self) -> usize {
48 self.rows
49 }
50
51 #[must_use]
53 pub fn is_empty(&self) -> bool {
54 self.rows == 0
55 }
56
57 #[must_use]
59 pub fn chunk_count(&self) -> usize {
60 self.chunks.len()
61 }
62
63 pub fn append(&mut self, chunk: Chunk) -> Result<()> {
72 if chunk.width() != self.types.len() {
73 return Err(Error::internal(format!(
74 "a chunk of {} columns appended to a table of {}",
75 chunk.width(),
76 self.types.len()
77 )));
78 }
79 for (index, (held, wanted)) in chunk.types().iter().zip(&self.types).enumerate() {
80 if held != wanted {
81 return Err(Error::internal(format!(
82 "column {index} of the chunk is {held} and the table's is {wanted}"
83 )));
84 }
85 }
86 if chunk.is_empty() {
87 return Ok(());
88 }
89 self.rows += chunk.len();
90 self.chunks.push(chunk);
91 Ok(())
92 }
93
94 pub fn append_rows(&mut self, rows: &[Vec<Value>]) -> Result<()> {
103 for (index, row) in rows.iter().enumerate() {
104 if row.len() != self.types.len() {
105 return Err(Error::internal(format!(
106 "row {index} has {} values and the table has {} columns",
107 row.len(),
108 self.types.len()
109 )));
110 }
111 }
112 for batch in rows.chunks(VECTOR_SIZE) {
113 let mut columns = Vec::with_capacity(self.types.len());
114 for (position, ty) in self.types.iter().enumerate() {
115 let down: Vec<Value> = batch.iter().map(|row| row[position].clone()).collect();
116 columns.push(Vector::from_values(ty.clone(), &down)?);
117 }
118 self.append(Chunk::with_rows(columns, batch.len())?)?;
119 }
120 Ok(())
121 }
122
123 pub fn read(&self, chunk: usize, columns: &[usize]) -> Result<Chunk> {
135 let held = self.chunks.get(chunk).ok_or_else(|| {
136 Error::internal(format!(
137 "chunk {chunk} of a table that has {} chunks",
138 self.chunks.len()
139 ))
140 })?;
141 let mut picked = Vec::with_capacity(columns.len());
142 for &column in columns {
143 picked.push(held.column(column)?.clone());
144 }
145 Chunk::with_rows(picked, held.len())
146 }
147
148 #[must_use]
150 pub fn chunk(&self, index: usize) -> Option<&Chunk> {
151 self.chunks.get(index)
152 }
153}
154
155#[cfg(test)]
156mod tests {
157 use super::*;
158
159 fn people() -> MemoryTable {
160 let mut table = MemoryTable::new(vec![LogicalType::Integer, LogicalType::Varchar]);
161 table
162 .append_rows(&[
163 vec![Value::Integer(1), Value::Varchar("ada".to_string())],
164 vec![Value::Integer(2), Value::Null],
165 vec![Value::Integer(3), Value::Varchar("grace".to_string())],
166 ])
167 .expect("three rows of the table's own types");
168 table
169 }
170
171 #[test]
172 fn rows_go_in_and_come_back_out() {
173 let table = people();
174 assert_eq!(table.len(), 3);
175 assert_eq!(table.chunk_count(), 1);
176 let chunk = table.read(0, &[0, 1]).expect("both columns of the only chunk");
177 assert_eq!(chunk.value_at(0, 1), Value::Varchar("ada".to_string()));
178 assert_eq!(chunk.value_at(1, 1), Value::Null);
179 assert_eq!(chunk.value_at(2, 0), Value::Integer(3));
180 }
181
182 #[test]
183 fn a_read_gives_back_only_the_columns_it_was_asked_for() {
184 let table = people();
185 let chunk = table.read(0, &[1]).expect("the second column");
186 assert_eq!(chunk.width(), 1);
187 assert_eq!(chunk.len(), 3);
188 assert_eq!(chunk.value_at(2, 0), Value::Varchar("grace".to_string()));
189 }
190
191 #[test]
193 fn a_read_of_no_columns_still_says_how_many_rows() {
194 let table = people();
195 let chunk = table.read(0, &[]).expect("no columns");
196 assert_eq!(chunk.width(), 0);
197 assert_eq!(chunk.len(), 3);
198 }
199
200 #[test]
201 fn more_rows_than_a_vector_become_more_than_one_chunk() {
202 let mut table = MemoryTable::new(vec![LogicalType::BigInt]);
203 let rows: Vec<Vec<Value>> =
204 (0..VECTOR_SIZE + 5).map(|n| vec![Value::BigInt(n as i64)]).collect();
205 table.append_rows(&rows).expect("bigints");
206 assert_eq!(table.len(), VECTOR_SIZE + 5);
207 assert_eq!(table.chunk_count(), 2);
208 let last = table.read(1, &[0]).expect("the second chunk");
209 assert_eq!(last.len(), 5);
210 assert_eq!(last.value_at(4, 0), Value::BigInt((VECTOR_SIZE + 4) as i64));
211 }
212
213 #[test]
214 fn a_chunk_of_the_wrong_types_is_caught() {
215 let mut table = MemoryTable::new(vec![LogicalType::Integer]);
216 let wrong = Chunk::new(vec![
217 Vector::from_values(LogicalType::Varchar, &[Value::Varchar("x".to_string())])
218 .expect("a string column"),
219 ])
220 .expect("one column");
221 let error = table.append(wrong).expect_err("a varchar is not an integer");
222 assert!(error.message().contains("column 0"), "{error}");
223 }
224
225 #[test]
226 fn a_row_of_the_wrong_width_is_caught() {
227 let mut table = MemoryTable::new(vec![LogicalType::Integer, LogicalType::Integer]);
228 let error =
229 table.append_rows(&[vec![Value::Integer(1)]]).expect_err("a row of one is not a row");
230 assert!(error.message().contains("row 0"), "{error}");
231 }
232
233 #[test]
234 fn an_empty_chunk_is_not_stored() {
235 let mut table = MemoryTable::new(vec![LogicalType::Integer]);
236 table.append(Chunk::empty(&[LogicalType::Integer])).expect("an empty chunk is allowed");
237 assert_eq!(table.chunk_count(), 0);
238 assert!(table.is_empty());
239 }
240}