use rudb_common::{Error, LogicalType, Result, Value};
use crate::string::StringColumn;
use crate::validity::Validity;
pub const VECTOR_SIZE: usize = 1024;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Form {
Flat,
Constant,
Sequence,
Dictionary,
}
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub enum Data {
Empty,
Bool(Vec<bool>),
Int8(Vec<i8>),
Int16(Vec<i16>),
Int32(Vec<i32>),
Int64(Vec<i64>),
Int128(Vec<i128>),
UInt8(Vec<u8>),
UInt16(Vec<u16>),
UInt32(Vec<u32>),
UInt64(Vec<u64>),
UInt128(Vec<u128>),
Float32(Vec<f32>),
Float64(Vec<f64>),
Interval(Vec<(i32, i32, i64)>),
Varlen(StringColumn),
}
impl Data {
#[must_use]
pub fn len(&self) -> usize {
match self {
Self::Empty => 0,
Self::Bool(v) => v.len(),
Self::Int8(v) => v.len(),
Self::Int16(v) => v.len(),
Self::Int32(v) => v.len(),
Self::Int64(v) => v.len(),
Self::Int128(v) => v.len(),
Self::UInt8(v) => v.len(),
Self::UInt16(v) => v.len(),
Self::UInt32(v) => v.len(),
Self::UInt64(v) => v.len(),
Self::UInt128(v) => v.len(),
Self::Float32(v) => v.len(),
Self::Float64(v) => v.len(),
Self::Interval(v) => v.len(),
Self::Varlen(v) => v.len(),
}
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
#[must_use]
pub fn signed_at(&self, index: usize) -> Option<i128> {
match self {
Self::Int8(v) => v.get(index).map(|&x| i128::from(x)),
Self::Int16(v) => v.get(index).map(|&x| i128::from(x)),
Self::Int32(v) => v.get(index).map(|&x| i128::from(x)),
Self::Int64(v) => v.get(index).map(|&x| i128::from(x)),
Self::Int128(v) => v.get(index).copied(),
_ => None,
}
}
#[must_use]
pub fn unsigned_at(&self, index: usize) -> Option<u128> {
match self {
Self::UInt8(v) => v.get(index).map(|&x| u128::from(x)),
Self::UInt16(v) => v.get(index).map(|&x| u128::from(x)),
Self::UInt32(v) => v.get(index).map(|&x| u128::from(x)),
Self::UInt64(v) => v.get(index).map(|&x| u128::from(x)),
Self::UInt128(v) => v.get(index).copied(),
_ => None,
}
}
#[must_use]
pub fn str_at(&self, index: usize) -> Option<&str> {
match self {
Self::Varlen(column) => column.get(index),
_ => None,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Vector {
ty: LogicalType,
len: usize,
validity: Validity,
body: Body,
}
#[derive(Debug, Clone, PartialEq)]
enum Body {
Flat(Data),
Constant(Box<Value>),
Sequence { start: i64, step: i64 },
Dictionary { codes: Vec<u32>, values: Box<Vector> },
}
impl Vector {
pub fn flat(ty: LogicalType, data: Data) -> Result<Self> {
let len = data.len();
if !matches!(data, Data::Empty) && layout_of(&data) != ty.physical() {
return Err(Error::internal(format!(
"a {ty} vector cannot hold {:?} data",
layout_of(&data)
)));
}
Ok(Self { ty, len, validity: Validity::AllValid, body: Body::Flat(data) })
}
#[must_use]
pub fn constant(ty: LogicalType, value: Value, len: usize) -> Self {
let validity = if value.is_null() { Validity::AllInvalid } else { Validity::AllValid };
Self { ty, len, validity, body: Body::Constant(Box::new(value)) }
}
#[must_use]
pub fn sequence(start: i64, step: i64, len: usize) -> Self {
Self {
ty: LogicalType::BigInt,
len,
validity: Validity::AllValid,
body: Body::Sequence { start, step },
}
}
pub fn dictionary(codes: Vec<u32>, values: Vector) -> Result<Self> {
if let Some(&bad) = codes.iter().find(|&&code| code as usize >= values.len()) {
return Err(Error::internal(format!(
"dictionary code {bad} is past the end of a {} value dictionary",
values.len()
)));
}
Ok(Self {
ty: values.ty.clone(),
len: codes.len(),
validity: Validity::AllValid,
body: Body::Dictionary { codes, values: Box::new(values) },
})
}
#[must_use]
pub fn with_validity(mut self, validity: Validity) -> Self {
self.validity = validity;
self
}
#[must_use]
pub fn logical_type(&self) -> &LogicalType {
&self.ty
}
#[must_use]
pub fn len(&self) -> usize {
self.len
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.len == 0
}
#[must_use]
pub fn validity(&self) -> &Validity {
&self.validity
}
#[must_use]
pub fn form(&self) -> Form {
match self.body {
Body::Flat(_) => Form::Flat,
Body::Constant(_) => Form::Constant,
Body::Sequence { .. } => Form::Sequence,
Body::Dictionary { .. } => Form::Dictionary,
}
}
#[must_use]
pub fn data(&self) -> Option<&Data> {
match &self.body {
Body::Flat(data) => Some(data),
_ => None,
}
}
#[must_use]
pub fn value_at(&self, index: usize) -> Value {
if index >= self.len || !self.validity.is_valid(index) {
return Value::Null;
}
match &self.body {
Body::Constant(value) => value.as_ref().clone(),
Body::Sequence { start, step } => Value::BigInt(start + step * index as i64),
Body::Dictionary { codes, values } => match codes.get(index) {
Some(&code) => values.value_at(code as usize),
None => Value::Null,
},
Body::Flat(data) => value_from(&self.ty, data, index),
}
}
pub fn iter(&self) -> impl Iterator<Item = Value> + '_ {
(0..self.len).map(|index| self.value_at(index))
}
pub fn flatten(&self) -> Result<Self> {
if let Body::Flat(_) = self.body {
return Ok(self.clone());
}
let mut data = empty_data_for(&self.ty)?;
for index in 0..self.len {
push_value(&mut data, &self.value_at(index))?;
}
let validity = Validity::from_iter(self.len, |index| self.validity.is_valid(index));
Ok(Self { ty: self.ty.clone(), len: self.len, validity, body: Body::Flat(data) })
}
}
fn layout_of(data: &Data) -> rudb_common::PhysicalType {
use rudb_common::PhysicalType as P;
match data {
Data::Empty => P::Empty,
Data::Bool(_) => P::Bool,
Data::Int8(_) => P::Int8,
Data::Int16(_) => P::Int16,
Data::Int32(_) => P::Int32,
Data::Int64(_) => P::Int64,
Data::Int128(_) => P::Int128,
Data::UInt8(_) => P::UInt8,
Data::UInt16(_) => P::UInt16,
Data::UInt32(_) => P::UInt32,
Data::UInt64(_) => P::UInt64,
Data::UInt128(_) => P::UInt128,
Data::Float32(_) => P::Float32,
Data::Float64(_) => P::Float64,
Data::Interval(_) => P::Interval,
Data::Varlen(_) => P::Varlen,
}
}
fn value_from(ty: &LogicalType, data: &Data, index: usize) -> Value {
let signed = || data.signed_at(index);
let unsigned = || data.unsigned_at(index);
let value = match ty {
LogicalType::Boolean => match data {
Data::Bool(v) => v.get(index).map(|&x| Value::Boolean(x)),
_ => None,
},
LogicalType::TinyInt => signed().and_then(|x| i8::try_from(x).ok()).map(Value::TinyInt),
LogicalType::SmallInt => signed().and_then(|x| i16::try_from(x).ok()).map(Value::SmallInt),
LogicalType::Integer => signed().and_then(|x| i32::try_from(x).ok()).map(Value::Integer),
LogicalType::BigInt => signed().and_then(|x| i64::try_from(x).ok()).map(Value::BigInt),
LogicalType::HugeInt => signed().map(Value::HugeInt),
LogicalType::UTinyInt => unsigned().and_then(|x| u8::try_from(x).ok()).map(Value::UTinyInt),
LogicalType::USmallInt => {
unsigned().and_then(|x| u16::try_from(x).ok()).map(Value::USmallInt)
}
LogicalType::UInteger => {
unsigned().and_then(|x| u32::try_from(x).ok()).map(Value::UInteger)
}
LogicalType::UBigInt => unsigned().and_then(|x| u64::try_from(x).ok()).map(Value::UBigInt),
LogicalType::UHugeInt => unsigned().map(Value::UHugeInt),
LogicalType::Float => match data {
Data::Float32(v) => v.get(index).map(|&x| Value::Float(x)),
_ => None,
},
LogicalType::Double => match data {
Data::Float64(v) => v.get(index).map(|&x| Value::Double(x)),
_ => None,
},
LogicalType::Decimal { width, scale } => {
signed().map(|unscaled| Value::Decimal { unscaled, width: *width, scale: *scale })
}
LogicalType::Varchar => data.str_at(index).map(|s| Value::Varchar(s.to_string())),
LogicalType::Blob | LogicalType::Bit => {
data.str_at(index).map(|s| Value::Blob(s.as_bytes().to_vec()))
}
LogicalType::Date => signed().and_then(|x| i32::try_from(x).ok()).map(Value::Date),
LogicalType::Time | LogicalType::TimeTz => {
signed().and_then(|x| i64::try_from(x).ok()).map(Value::Time)
}
LogicalType::Timestamp
| LogicalType::TimestampS
| LogicalType::TimestampMs
| LogicalType::TimestampNs
| LogicalType::TimestampTz => {
signed().and_then(|x| i64::try_from(x).ok()).map(Value::Timestamp)
}
LogicalType::Interval => match data {
Data::Interval(v) => {
v.get(index).map(|&(months, days, micros)| Value::Interval { months, days, micros })
}
_ => None,
},
_ => None,
};
value.unwrap_or(Value::Null)
}
fn empty_data_for(ty: &LogicalType) -> Result<Data> {
use rudb_common::PhysicalType as P;
Ok(match ty.physical() {
P::Empty => Data::Empty,
P::Bool => Data::Bool(Vec::new()),
P::Int8 => Data::Int8(Vec::new()),
P::Int16 => Data::Int16(Vec::new()),
P::Int32 => Data::Int32(Vec::new()),
P::Int64 => Data::Int64(Vec::new()),
P::Int128 => Data::Int128(Vec::new()),
P::UInt8 => Data::UInt8(Vec::new()),
P::UInt16 => Data::UInt16(Vec::new()),
P::UInt32 => Data::UInt32(Vec::new()),
P::UInt64 => Data::UInt64(Vec::new()),
P::UInt128 => Data::UInt128(Vec::new()),
P::Float32 => Data::Float32(Vec::new()),
P::Float64 => Data::Float64(Vec::new()),
P::Interval => Data::Interval(Vec::new()),
P::Varlen => Data::Varlen(StringColumn::new()),
other => {
return Err(Error::not_implemented(format!(
"a flat vector of {other:?} data, which arrives with the storage layer"
)));
}
})
}
fn push_value(data: &mut Data, value: &Value) -> Result<()> {
macro_rules! push {
($vec:expr, $variant:path, $zero:expr) => {
match value {
Value::Null => $vec.push($zero),
$variant(x) => $vec.push(*x),
other => {
return Err(Error::internal(format!(
"{other:?} does not belong in this vector"
)));
}
}
};
}
match data {
Data::Empty => {}
Data::Bool(v) => push!(v, Value::Boolean, false),
Data::Int8(v) => push!(v, Value::TinyInt, 0),
Data::Int16(v) => push!(v, Value::SmallInt, 0),
Data::Int32(v) => match value {
Value::Null => v.push(0),
Value::Integer(x) | Value::Date(x) => v.push(*x),
other => return Err(Error::internal(format!("{other:?} is not a 32 bit value"))),
},
Data::Int64(v) => match value {
Value::Null => v.push(0),
Value::BigInt(x) | Value::Time(x) | Value::Timestamp(x) => v.push(*x),
other => return Err(Error::internal(format!("{other:?} is not a 64 bit value"))),
},
Data::Int128(v) => match value {
Value::Null => v.push(0),
Value::HugeInt(x) => v.push(*x),
Value::Decimal { unscaled, .. } => v.push(*unscaled),
other => return Err(Error::internal(format!("{other:?} is not a 128 bit value"))),
},
Data::UInt8(v) => push!(v, Value::UTinyInt, 0),
Data::UInt16(v) => push!(v, Value::USmallInt, 0),
Data::UInt32(v) => push!(v, Value::UInteger, 0),
Data::UInt64(v) => push!(v, Value::UBigInt, 0),
Data::UInt128(v) => push!(v, Value::UHugeInt, 0),
Data::Float32(v) => push!(v, Value::Float, 0.0),
Data::Float64(v) => push!(v, Value::Double, 0.0),
Data::Interval(v) => match value {
Value::Null => v.push((0, 0, 0)),
Value::Interval { months, days, micros } => v.push((*months, *days, *micros)),
other => return Err(Error::internal(format!("{other:?} is not an interval"))),
},
Data::Varlen(column) => match value {
Value::Null => {
column.push("");
}
Value::Varchar(text) => {
column.push(text);
}
Value::Blob(bytes) => match std::str::from_utf8(bytes) {
Ok(text) => {
column.push(text);
}
Err(_) => {
return Err(Error::not_implemented(
"a blob that is not valid UTF-8, which needs the byte column from M2",
));
}
},
other => return Err(Error::internal(format!("{other:?} is not a string"))),
},
}
Ok(())
}
#[cfg(test)]
mod tests {
use rudb_common::{LogicalType, Value};
use super::{Data, Form, VECTOR_SIZE, Vector};
use crate::string::StringColumn;
use crate::validity::Validity;
fn integers(values: &[i32]) -> Vector {
Vector::flat(LogicalType::Integer, Data::Int32(values.to_vec())).unwrap()
}
#[test]
fn the_vector_size_is_the_one_the_design_is_built_around() {
assert_eq!(VECTOR_SIZE, 1024);
assert_eq!(VECTOR_SIZE / 64, 16);
}
#[test]
fn a_flat_vector_reads_back_what_was_put_in_it() {
let vector = integers(&[1, 2, 3]);
assert_eq!(vector.form(), Form::Flat);
assert_eq!(vector.len(), 3);
assert_eq!(vector.value_at(1), Value::Integer(2));
assert_eq!(
vector.iter().collect::<Vec<_>>(),
vec![Value::Integer(1), Value::Integer(2), Value::Integer(3)]
);
}
#[test]
fn a_type_that_does_not_match_its_layout_is_refused_at_construction() {
let wrong = Vector::flat(LogicalType::Varchar, Data::Int32(vec![1]));
assert!(wrong.is_err());
let right = Vector::flat(LogicalType::Date, Data::Int32(vec![1]));
assert!(right.is_ok(), "a date is stored in an i32 and that has to be allowed");
}
#[test]
fn a_constant_vector_costs_one_value_whatever_its_length() {
let vector = Vector::constant(LogicalType::Integer, Value::Integer(7), VECTOR_SIZE);
assert_eq!(vector.form(), Form::Constant);
assert_eq!(vector.len(), VECTOR_SIZE);
assert_eq!(vector.value_at(0), Value::Integer(7));
assert_eq!(vector.value_at(VECTOR_SIZE - 1), Value::Integer(7));
assert_eq!(vector.value_at(VECTOR_SIZE), Value::Null, "past the end is null, not a panic");
}
#[test]
fn a_constant_null_is_all_invalid_without_being_told() {
let vector = Vector::constant(LogicalType::Integer, Value::Null, 8);
assert_eq!(vector.validity(), &Validity::AllInvalid);
assert_eq!(vector.value_at(3), Value::Null);
}
#[test]
fn a_sequence_vector_is_sixteen_bytes_of_row_identifiers() {
let vector = Vector::sequence(100, 1, VECTOR_SIZE);
assert_eq!(vector.form(), Form::Sequence);
assert_eq!(vector.value_at(0), Value::BigInt(100));
assert_eq!(vector.value_at(923), Value::BigInt(1023));
let stepped = Vector::sequence(0, 5, 4);
assert_eq!(
stepped.iter().collect::<Vec<_>>(),
vec![Value::BigInt(0), Value::BigInt(5), Value::BigInt(10), Value::BigInt(15)]
);
}
#[test]
fn a_dictionary_vector_reads_through_its_codes() {
let mut column = StringColumn::new();
column.push("red");
column.push("green");
let values = Vector::flat(LogicalType::Varchar, Data::Varlen(column)).unwrap();
let vector = Vector::dictionary(vec![0, 1, 1, 0], values).unwrap();
assert_eq!(vector.form(), Form::Dictionary);
assert_eq!(vector.logical_type(), &LogicalType::Varchar);
assert_eq!(vector.value_at(2), Value::Varchar("green".into()));
assert_eq!(vector.len(), 4);
}
#[test]
fn a_dictionary_code_past_the_end_is_refused() {
let values = integers(&[1, 2]);
assert!(Vector::dictionary(vec![0, 2], values).is_err());
}
#[test]
fn every_form_flattens_to_the_same_values_it_reads_out() {
let mut column = StringColumn::new();
column.push("alpha");
column.push("beta");
let dictionary = Vector::dictionary(
vec![1, 0, 1],
Vector::flat(LogicalType::Varchar, Data::Varlen(column)).unwrap(),
)
.unwrap();
let cases = [
Vector::constant(LogicalType::Integer, Value::Integer(3), 5),
Vector::sequence(7, -2, 5),
dictionary,
];
for vector in cases {
let flat = vector.flatten().unwrap();
assert_eq!(flat.form(), Form::Flat);
assert_eq!(flat.len(), vector.len());
for index in 0..vector.len() {
assert_eq!(flat.value_at(index), vector.value_at(index), "at {index}");
}
}
}
#[test]
fn a_null_still_occupies_a_position_after_flattening() {
let vector = Vector::sequence(0, 1, 4).with_validity(Validity::from_iter(4, |i| i != 1));
let flat = vector.flatten().unwrap();
assert_eq!(flat.value_at(0), Value::BigInt(0));
assert_eq!(flat.value_at(1), Value::Null);
assert_eq!(flat.value_at(2), Value::BigInt(2));
assert_eq!(flat.value_at(3), Value::BigInt(3));
}
#[test]
fn flattening_a_flat_vector_is_the_same_vector() {
let vector = integers(&[1, 2, 3]);
assert_eq!(vector.flatten().unwrap(), vector);
}
#[test]
fn a_decimal_reads_its_width_and_scale_from_the_type_and_not_the_data() {
let ty = LogicalType::decimal(9, 2).unwrap();
let vector = Vector::flat(ty, Data::Int32(vec![1234])).unwrap();
assert_eq!(vector.value_at(0), Value::Decimal { unscaled: 1234, width: 9, scale: 2 });
assert_eq!(vector.value_at(0).to_string(), "12.34");
}
}