use std::fmt;
use std::ops::Index;
use crate::{CompactArc, CompactVec, DataType, Error, LogicalTypeRef, Result, Value};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct RowColumnRef<'a> {
pub name: &'a str,
pub data_type: DataType,
pub logical_type: LogicalTypeRef,
pub nullable: bool,
}
impl<'a> RowColumnRef<'a> {
pub const fn new(name: &'a str, data_type: DataType, nullable: bool) -> Self {
Self {
name,
data_type,
logical_type: LogicalTypeRef::Builtin(data_type),
nullable,
}
}
pub const fn with_logical_type(mut self, logical_type: LogicalTypeRef) -> Self {
self.logical_type = logical_type;
self
}
}
pub trait RowSchema {
fn row_column_count(&self) -> usize;
fn row_column(&self, index: usize) -> Option<RowColumnRef<'_>>;
}
#[derive(Debug, Clone)]
enum RowStorage {
Shared(CompactArc<[Value]>),
Owned(CompactVec<Value>),
}
impl Default for RowStorage {
fn default() -> Self {
RowStorage::Owned(CompactVec::new())
}
}
impl PartialEq for RowStorage {
#[inline]
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(RowStorage::Shared(a), RowStorage::Shared(b)) => {
CompactArc::ptr_eq(a, b) || a.as_ref() == b.as_ref()
}
(RowStorage::Owned(a), RowStorage::Owned(b)) => a.as_slice() == b.as_slice(),
(RowStorage::Shared(a), RowStorage::Owned(b)) => a.as_ref() == b.as_slice(),
(RowStorage::Owned(a), RowStorage::Shared(b)) => a.as_slice() == b.as_ref(),
}
}
}
impl RowStorage {
#[inline(always)]
fn get(&self, index: usize) -> Option<&Value> {
match self {
RowStorage::Shared(arc) => arc.get(index),
RowStorage::Owned(vec) => vec.get(index),
}
}
#[inline(always)]
fn len(&self) -> usize {
match self {
RowStorage::Shared(arc) => arc.len(),
RowStorage::Owned(vec) => vec.len(),
}
}
#[inline]
fn is_empty(&self) -> bool {
self.len() == 0
}
#[inline]
fn make_mut(&mut self) -> &mut CompactVec<Value> {
match self {
RowStorage::Owned(vec) => vec,
RowStorage::Shared(arc) => {
let len = arc.len();
let mut vec = CompactVec::with_capacity(len);
vec.extend_clone(arc);
*self = RowStorage::Owned(vec);
match self {
RowStorage::Owned(vec) => vec,
_ => unreachable!(),
}
}
}
}
#[inline]
fn into_vec(self) -> Vec<Value> {
match self {
RowStorage::Owned(vec) => vec.into_vec(),
RowStorage::Shared(arc) => arc.iter().cloned().collect(),
}
}
}
#[derive(Debug, Clone, PartialEq, Default)]
pub struct Row {
storage: RowStorage,
}
pub struct RowIter<'a> {
inner: std::slice::Iter<'a, Value>,
}
impl<'a> Iterator for RowIter<'a> {
type Item = &'a Value;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.inner.next()
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}
}
impl<'a> ExactSizeIterator for RowIter<'a> {
fn len(&self) -> usize {
self.inner.len()
}
}
impl<'a> DoubleEndedIterator for RowIter<'a> {
fn next_back(&mut self) -> Option<Self::Item> {
self.inner.next_back()
}
}
pub struct RowIterMut<'a> {
inner: std::slice::IterMut<'a, Value>,
}
impl<'a> Iterator for RowIterMut<'a> {
type Item = &'a mut Value;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.inner.next()
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}
}
impl<'a> ExactSizeIterator for RowIterMut<'a> {
fn len(&self) -> usize {
self.inner.len()
}
}
impl Row {
#[inline]
pub fn new() -> Self {
Self {
storage: RowStorage::Owned(CompactVec::new()),
}
}
#[inline]
pub fn with_capacity(capacity: usize) -> Self {
Self {
storage: RowStorage::Owned(CompactVec::with_capacity(capacity)),
}
}
#[inline]
pub fn from_values(values: Vec<Value>) -> Self {
Self {
storage: RowStorage::Owned(CompactVec::from_vec(values)),
}
}
#[inline]
pub fn from_compact_vec(values: CompactVec<Value>) -> Self {
Self {
storage: RowStorage::Owned(values),
}
}
#[inline]
pub fn from_arc(values: CompactArc<[Value]>) -> Self {
Self {
storage: RowStorage::Shared(values),
}
}
#[inline]
pub fn from_combined(left: &Row, right: &Row) -> Self {
let total_len = left.len() + right.len();
let mut values = CompactVec::with_capacity(total_len);
values.extend_clone(left.as_slice());
values.extend_clone(right.as_slice());
Self {
storage: RowStorage::Owned(values),
}
}
#[inline]
pub fn combine_into_owned(&mut self, left: Row, right: Row) {
let total_len = left.len() + right.len();
let vec = self.storage.make_mut();
vec.clear();
vec.reserve(total_len);
match left.storage {
RowStorage::Owned(left_vec) => vec.extend(left_vec),
RowStorage::Shared(arc) => vec.extend_clone(&arc),
}
match right.storage {
RowStorage::Owned(right_vec) => vec.extend(right_vec),
RowStorage::Shared(arc) => vec.extend_clone(&arc),
}
}
#[inline]
pub fn from_combined_clone_move(left: &Row, right: Row) -> Self {
let total_len = left.len() + right.len();
let mut values = CompactVec::with_capacity(total_len);
values.extend_clone(left.as_slice());
match right.storage {
RowStorage::Owned(right_vec) => values.extend(right_vec),
RowStorage::Shared(arc) => values.extend_clone(&arc),
}
Self {
storage: RowStorage::Owned(values),
}
}
#[inline]
pub fn from_combined_owned(left: Row, right: Row) -> Self {
match (left.storage, right.storage) {
(RowStorage::Owned(mut left_vec), RowStorage::Owned(right_vec)) => {
left_vec.reserve(right_vec.len());
left_vec.extend(right_vec);
Self {
storage: RowStorage::Owned(left_vec),
}
}
(left_storage, right_storage) => {
let left_len = left_storage.len();
let right_len = right_storage.len();
let mut values = CompactVec::with_capacity(left_len + right_len);
match left_storage {
RowStorage::Owned(v) => values.extend(v),
RowStorage::Shared(a) => values.extend_clone(&a),
}
match right_storage {
RowStorage::Owned(v) => values.extend(v),
RowStorage::Shared(a) => values.extend_clone(&a),
}
Self {
storage: RowStorage::Owned(values),
}
}
}
}
#[inline]
pub fn null_row<S: RowSchema + ?Sized>(schema: &S) -> Self {
let values: CompactVec<Value> = (0..schema.row_column_count())
.map(|index| {
let column = schema
.row_column(index)
.expect("RowSchema must resolve every declared column index");
Value::null(column.data_type)
})
.collect();
Self {
storage: RowStorage::Owned(values),
}
}
#[inline(always)]
pub fn len(&self) -> usize {
self.storage.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.storage.is_empty()
}
#[inline(always)]
pub fn get(&self, index: usize) -> Option<&Value> {
self.storage.get(index)
}
#[inline]
pub fn get_mut(&mut self, index: usize) -> Option<&mut Value> {
self.storage.make_mut().get_mut(index)
}
pub fn set(&mut self, index: usize, value: Value) -> Result<()> {
let vec = self.storage.make_mut();
if index >= vec.len() {
return Err(Error::Internal {
message: format!("row index {} out of bounds (len={})", index, vec.len()),
});
}
vec[index] = value;
Ok(())
}
#[inline]
pub fn push(&mut self, value: Value) {
self.storage.make_mut().push(value);
}
#[inline]
pub fn pop(&mut self) -> Option<Value> {
self.storage.make_mut().pop()
}
#[inline]
#[doc(hidden)]
pub fn remove_column(&mut self, index: usize) -> Option<Value> {
(index < self.len()).then(|| self.storage.make_mut().remove(index))
}
#[inline]
pub fn truncate(&mut self, len: usize) {
self.storage.make_mut().truncate(len);
}
#[inline]
pub fn clear(&mut self) {
self.storage.make_mut().clear();
}
#[inline]
pub fn take_and_clear(&mut self) -> Row {
match &mut self.storage {
RowStorage::Owned(vec) => {
let cap = vec.capacity();
let values = std::mem::replace(vec, CompactVec::with_capacity(cap));
Row {
storage: RowStorage::Owned(values),
}
}
RowStorage::Shared(arc) => {
let result = Row {
storage: RowStorage::Shared(arc.clone()),
};
*self = Row::new();
result
}
}
}
#[inline]
pub fn reserve(&mut self, additional: usize) {
self.storage.make_mut().reserve(additional);
}
#[inline]
pub fn extend_from_slice(&mut self, other: &[Value]) {
self.storage.make_mut().extend_clone(other);
}
#[inline]
pub fn extend_into_compact_vec(self, target: &mut CompactVec<Value>) {
match self.storage {
RowStorage::Owned(vec) => target.extend(vec),
RowStorage::Shared(arc) => target.extend_clone(&arc),
}
}
#[inline(always)]
pub fn iter(&self) -> RowIter<'_> {
RowIter {
inner: match &self.storage {
RowStorage::Shared(arc) => arc.iter(),
RowStorage::Owned(vec) => vec.iter(),
},
}
}
#[inline]
pub fn iter_mut(&mut self) -> RowIterMut<'_> {
RowIterMut {
inner: self.storage.make_mut().iter_mut(),
}
}
#[inline]
pub fn into_values(self) -> Vec<Value> {
self.storage.into_vec()
}
#[inline]
pub fn take_first_value(self) -> Option<Value> {
match self.storage {
RowStorage::Owned(mut vec) => {
if vec.is_empty() {
None
} else {
Some(vec.swap_remove(0))
}
}
RowStorage::Shared(arc) => arc.first().cloned(),
}
}
#[inline]
pub fn is_shared(&self) -> bool {
matches!(self.storage, RowStorage::Shared(_))
}
#[inline]
pub fn is_owned(&self) -> bool {
matches!(self.storage, RowStorage::Owned(_))
}
#[inline]
pub fn into_arc(self) -> CompactArc<[Value]> {
match self.storage {
RowStorage::Shared(arc) => arc,
RowStorage::Owned(vec) => CompactArc::from_compact_vec(vec),
}
}
#[inline]
pub fn as_arc(&self) -> Option<&CompactArc<[Value]>> {
match &self.storage {
RowStorage::Shared(arc) => Some(arc),
RowStorage::Owned(_) => None,
}
}
#[inline]
pub fn as_slice(&self) -> &[Value] {
match &self.storage {
RowStorage::Shared(arc) => arc,
RowStorage::Owned(vec) => vec.as_slice(),
}
}
#[inline]
pub fn select_columns(&self, indices: &[usize]) -> Result<Row> {
self.validate_projection_indices(indices)?;
let mut values = CompactVec::with_capacity(indices.len());
for &idx in indices {
values.push(
self.storage
.get(idx)
.expect("projection index validated")
.clone(),
);
}
Ok(Row::from_compact_vec(values))
}
#[inline]
fn validate_projection_indices(&self, indices: &[usize]) -> Result<()> {
let len = self.len();
if let Some(&idx) = indices.iter().find(|&&idx| idx >= len) {
return Err(Error::Internal {
message: format!("column index {} out of bounds (len={})", idx, len),
});
}
Ok(())
}
#[inline]
pub fn take_columns(self, indices: &[usize]) -> Result<Row> {
self.validate_projection_indices(indices)?;
let is_prefix = !indices.is_empty() && indices.iter().enumerate().all(|(i, &idx)| i == idx);
if is_prefix {
match self.storage {
RowStorage::Owned(mut vec) => {
vec.truncate(indices.len());
return Ok(Row {
storage: RowStorage::Owned(vec),
});
}
RowStorage::Shared(ref arc) if indices.len() == arc.len() => {
return Ok(self);
}
RowStorage::Shared(arc) => {
let values: CompactVec<Value> = arc[..indices.len()].iter().cloned().collect();
return Ok(Row {
storage: RowStorage::Owned(values),
});
}
}
}
let mut values = CompactVec::with_capacity(indices.len());
let slice = self.as_slice();
for &idx in indices {
values.push(slice[idx].clone());
}
Ok(Row {
storage: RowStorage::Owned(values),
})
}
pub fn validate<S: RowSchema + ?Sized>(&self, schema: &S) -> Result<()> {
let len = self.len();
let column_count = schema.row_column_count();
if len != column_count {
return Err(Error::table_columns_not_match(column_count, len));
}
for (i, value) in self.iter().enumerate() {
let col = schema
.row_column(i)
.expect("RowSchema must resolve every declared column index");
if value.is_null() && !col.nullable {
return Err(Error::not_null_constraint(col.name));
}
if !value.is_null() {
let value_type = value.logical_type();
if value_type != col.logical_type {
let compatible = matches!(
(value_type, col.logical_type),
(
LogicalTypeRef::Builtin(DataType::Integer),
LogicalTypeRef::Builtin(DataType::Float)
) | (
LogicalTypeRef::Builtin(DataType::Float),
LogicalTypeRef::Builtin(DataType::Integer)
)
);
if !compatible {
return Err(Error::type_conversion(
format!("column {} at index {}: {:?}", col.name, i, value_type),
format!("{:?}", col.logical_type),
));
}
}
}
}
Ok(())
}
#[inline]
pub fn clone_subset(&self, indices: &[usize]) -> Result<Row> {
self.select_columns(indices)
}
pub fn concat(&self, other: &Row) -> Row {
let total_len = self.len() + other.len();
let mut values = CompactVec::with_capacity(total_len);
values.extend(self.iter().cloned());
values.extend(other.iter().cloned());
Row::from_compact_vec(values)
}
pub fn repeat(value: Value, count: usize) -> Row {
let mut values = CompactVec::with_capacity(count);
for _ in 0..count {
values.push(value.clone());
}
Row::from_compact_vec(values)
}
#[inline]
pub fn is_inline(&self) -> bool {
self.is_owned()
}
#[inline]
pub fn clear_inline(&mut self) {
self.clear();
}
#[inline]
pub fn push_inline(&mut self, value: Value) {
self.push(value);
}
#[inline]
pub fn reserve_inline(&mut self, capacity: usize) {
let vec = self.storage.make_mut();
if vec.capacity() < capacity {
vec.reserve(capacity - vec.len());
}
}
}
impl Index<usize> for Row {
type Output = Value;
#[inline]
fn index(&self, index: usize) -> &Self::Output {
self.storage.get(index).expect("row index out of bounds")
}
}
impl FromIterator<Value> for Row {
fn from_iter<I: IntoIterator<Item = Value>>(iter: I) -> Self {
Row::from_compact_vec(iter.into_iter().collect())
}
}
impl IntoIterator for Row {
type Item = Value;
type IntoIter = std::vec::IntoIter<Value>;
fn into_iter(self) -> Self::IntoIter {
self.storage.into_vec().into_iter()
}
}
impl<'a> IntoIterator for &'a Row {
type Item = &'a Value;
type IntoIter = RowIter<'a>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl From<Vec<Value>> for Row {
fn from(values: Vec<Value>) -> Self {
Row::from_values(values)
}
}
impl From<CompactArc<[Value]>> for Row {
fn from(values: CompactArc<[Value]>) -> Self {
Row::from_arc(values)
}
}
impl fmt::Display for Row {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "(")?;
for (i, value) in self.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}", value)?;
}
write!(f, ")")
}
}
#[macro_export]
macro_rules! row {
() => {
$crate::Row::new()
};
($($value:expr),+ $(,)?) => {
$crate::Row::from_values(vec![$($crate::Value::from($value)),+])
};
}
#[cfg(test)]
mod tests {
use super::*;
use crate::CompactArc;
struct TestSchema(Vec<(&'static str, DataType, bool)>);
impl RowSchema for TestSchema {
fn row_column_count(&self) -> usize {
self.0.len()
}
fn row_column(&self, index: usize) -> Option<RowColumnRef<'_>> {
self.0
.get(index)
.map(|(name, data_type, nullable)| RowColumnRef::new(name, *data_type, *nullable))
}
}
fn create_test_schema() -> TestSchema {
TestSchema(vec![
("id", DataType::Integer, false),
("name", DataType::Text, false),
("email", DataType::Text, true),
])
}
#[test]
fn test_row_creation() {
let row = Row::new();
assert!(row.is_empty());
assert_eq!(row.len(), 0);
let row = Row::with_capacity(10);
assert!(row.is_empty());
}
#[test]
fn test_row_from_values() {
let values = vec![
Value::integer(1),
Value::text("hello"),
Value::null(DataType::Text),
];
let row = Row::from_values(values);
assert_eq!(row.len(), 3);
assert!(row.is_owned());
}
#[test]
fn test_row_from_arc() {
let values: CompactArc<[Value]> =
CompactArc::from(vec![Value::integer(1), Value::text("hello")]);
let row = Row::from_arc(values);
assert_eq!(row.len(), 2);
assert!(row.is_shared());
let row2 = row.clone();
assert_eq!(row2.len(), 2);
assert_eq!(row, row2);
assert!(row2.is_shared());
}
#[test]
fn test_row_push_pop() {
let mut row = Row::new();
row.push(Value::integer(1));
row.push(Value::text("hello"));
assert_eq!(row.len(), 2);
let popped = row.pop();
assert_eq!(popped, Some(Value::text("hello")));
assert_eq!(row.len(), 1);
}
#[test]
fn test_row_copy_on_write() {
let values: CompactArc<[Value]> =
CompactArc::from(vec![Value::integer(1), Value::text("hello")]);
let mut row = Row::from_arc(values);
assert!(row.is_shared());
row.push(Value::integer(2));
assert_eq!(row.len(), 3);
assert!(row.is_owned()); }
#[test]
fn test_row_get_set() {
let mut row = Row::from_values(vec![Value::integer(1), Value::text("hello")]);
assert_eq!(row.get(0), Some(&Value::integer(1)));
assert_eq!(row.get(1), Some(&Value::text("hello")));
assert_eq!(row.get(2), None);
row.set(1, Value::text("world")).unwrap();
assert_eq!(row.get(1), Some(&Value::text("world")));
assert!(row.set(10, Value::integer(0)).is_err());
}
#[test]
fn test_row_index() {
let row = Row::from_values(vec![Value::integer(1), Value::text("hello")]);
assert_eq!(row[0], Value::integer(1));
assert_eq!(row[1], Value::text("hello"));
}
#[test]
fn test_row_iteration() {
let row = Row::from_values(vec![
Value::integer(1),
Value::integer(2),
Value::integer(3),
]);
let sum: i64 = row.iter().filter_map(|v| v.as_int64()).sum();
assert_eq!(sum, 6);
}
#[test]
fn test_row_select_columns() {
let row = Row::from_values(vec![
Value::integer(1),
Value::text("hello"),
Value::float(3.5),
Value::boolean(true),
]);
let selected = row.select_columns(&[0, 2]).unwrap();
assert_eq!(selected.len(), 2);
assert_eq!(selected[0], Value::integer(1));
assert_eq!(selected[1], Value::float(3.5));
assert!(row.select_columns(&[0, 10]).is_err());
}
#[test]
fn test_row_validate() {
let schema = create_test_schema();
let row = Row::from_values(vec![
Value::integer(1),
Value::text("Alice"),
Value::null(DataType::Text),
]);
assert!(row.validate(&schema).is_ok());
let row = Row::from_values(vec![Value::integer(1)]);
assert!(row.validate(&schema).is_err());
let row = Row::from_values(vec![
Value::integer(1),
Value::null(DataType::Text), Value::null(DataType::Text),
]);
let err = row.validate(&schema).unwrap_err();
assert!(matches!(err, Error::NotNullConstraint { .. }));
}
#[test]
fn test_row_null_row() {
let schema = create_test_schema();
let row = Row::null_row(&schema);
assert_eq!(row.len(), 3);
assert!(row[0].is_null());
assert!(row[1].is_null());
assert!(row[2].is_null());
}
#[test]
fn test_row_concat() {
let row1 = Row::from_values(vec![Value::integer(1), Value::integer(2)]);
let row2 = Row::from_values(vec![Value::integer(3), Value::integer(4)]);
let combined = row1.concat(&row2);
assert_eq!(combined.len(), 4);
assert_eq!(combined[0], Value::integer(1));
assert_eq!(combined[3], Value::integer(4));
}
#[test]
fn test_row_repeat() {
let row = Row::repeat(Value::integer(0), 5);
assert_eq!(row.len(), 5);
for v in row.iter() {
assert_eq!(*v, Value::integer(0));
}
}
#[test]
fn test_row_from_iterator() {
let row: Row = vec![Value::integer(1), Value::integer(2), Value::integer(3)]
.into_iter()
.collect();
assert_eq!(row.len(), 3);
}
#[test]
fn test_row_display() {
let row = Row::from_values(vec![
Value::integer(1),
Value::text("hello"),
Value::null(DataType::Text),
]);
assert_eq!(row.to_string(), "(1, hello, NULL)");
let empty = Row::new();
assert_eq!(empty.to_string(), "()");
}
#[test]
fn test_row_clone_subset() {
let row = Row::from_values(vec![
Value::integer(1),
Value::text("hello"),
Value::float(3.5),
]);
let subset = row.clone_subset(&[2, 0]).unwrap();
assert_eq!(subset.len(), 2);
assert_eq!(subset[0], Value::float(3.5));
assert_eq!(subset[1], Value::integer(1));
}
#[test]
fn test_row_projection_helpers_reject_invalid_indices_consistently() {
let row = Row::from_values(vec![Value::integer(1), Value::text("hello")]);
let invalid = [0, 2];
assert!(row.select_columns(&invalid).is_err());
assert!(row.clone().take_columns(&invalid).is_err());
assert!(row.clone_subset(&invalid).is_err());
}
#[test]
fn test_row_equality() {
let row1 = Row::from_values(vec![Value::integer(1), Value::text("hello")]);
let row2 = Row::from_values(vec![Value::integer(1), Value::text("hello")]);
let row3 = Row::from_values(vec![Value::integer(1), Value::text("world")]);
assert_eq!(row1, row2);
assert_ne!(row1, row3);
}
#[test]
fn test_row_into_values() {
let row = Row::from_values(vec![Value::integer(1), Value::text("hello")]);
let values = row.into_values();
assert_eq!(values.len(), 2);
assert_eq!(values[0], Value::integer(1));
}
#[test]
fn test_row_into_arc() {
let row = Row::from_values(vec![Value::integer(1), Value::text("hello")]);
let arc = row.into_arc();
assert_eq!(arc.len(), 2);
let row2 = Row::from_arc(CompactArc::clone(&arc));
let arc2 = row2.into_arc();
assert!(CompactArc::ptr_eq(&arc, &arc2));
}
#[test]
fn test_row_combined() {
let left = Row::from_values(vec![Value::integer(1), Value::integer(2)]);
let right = Row::from_values(vec![Value::integer(3), Value::integer(4)]);
let combined = Row::from_combined(&left, &right);
assert_eq!(combined.len(), 4);
assert_eq!(combined[0], Value::integer(1));
assert_eq!(combined[3], Value::integer(4));
}
#[test]
fn test_shared_owned_equality() {
let owned = Row::from_values(vec![Value::integer(1), Value::text("hello")]);
let shared = Row::from_arc(CompactArc::from(vec![
Value::integer(1),
Value::text("hello"),
]));
assert_eq!(owned, shared);
}
}