use rudb_common::{Error, LogicalType, Result, Value};
use crate::buffer::Buffer;
use crate::string::{StringColumn, StringView};
use crate::validity::Validity;
pub const VECTOR_SIZE: usize = 1024;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum Form {
Flat,
Constant,
Sequence,
Dictionary,
}
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub enum Data {
Empty,
Bool(Buffer<bool>),
Int8(Buffer<i8>),
Int16(Buffer<i16>),
Int32(Buffer<i32>),
Int64(Buffer<i64>),
Int128(Buffer<i128>),
UInt8(Buffer<u8>),
UInt16(Buffer<u16>),
UInt32(Buffer<u32>),
UInt64(Buffer<u64>),
UInt128(Buffer<u128>),
Float32(Buffer<f32>),
Float64(Buffer<f64>),
Interval(Buffer<(i32, i32, i64)>),
Varlen(StringColumn),
}
impl Data {
#[must_use]
pub fn len(&self) -> usize {
macro_rules! lengths {
($(($variant:ident, $native:ty, $zero:expr)),+ $(,)?) => {
match self {
Self::Empty => 0,
$(Self::$variant(values) => values.len(),)+
}
};
}
crate::for_each_layout!(all, lengths)
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
#[must_use]
pub fn signed_at(&self, index: usize) -> Option<i128> {
macro_rules! widened {
($(($variant:ident, $native:ty, $zero:expr)),+ $(,)?) => {
match self {
$(Self::$variant(v) => v.get(index).map(|&x| i128::from(x)),)+
_ => None,
}
};
}
crate::for_each_layout!(signed, widened)
}
#[must_use]
pub fn unsigned_at(&self, index: usize) -> Option<u128> {
macro_rules! widened {
($(($variant:ident, $native:ty, $zero:expr)),+ $(,)?) => {
match self {
$(Self::$variant(v) => v.get(index).map(|&x| u128::from(x)),)+
_ => None,
}
};
}
crate::for_each_layout!(unsigned, widened)
}
#[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) })
}
pub fn from_values(ty: LogicalType, values: &[Value]) -> Result<Self> {
let mut data = empty_data_for(&ty)?;
for value in values {
push_value(&mut data, value)?;
}
let validity = Validity::from_iter(values.len(), |index| !values[index].is_null());
Ok(Self { ty, len: values.len(), validity, 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()
)));
}
let (codes, values) = compose(codes, values);
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 constant_value(&self) -> Option<&Value> {
match &self.body {
Body::Constant(value) => Some(value.as_ref()),
_ => None,
}
}
#[must_use]
pub fn dictionary_parts(&self) -> Option<(&[u32], &Self)> {
match &self.body {
Body::Dictionary { codes, values } => Some((codes, values.as_ref())),
_ => None,
}
}
#[must_use]
pub fn sequence_parts(&self) -> Option<(i64, i64)> {
match self.body {
Body::Sequence { start, step } => Some((start, step)),
_ => 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());
}
self.copied((0..self.len).collect(), false)
}
pub fn gather(&self, indices: &[u32]) -> Result<Self> {
self.copied(indices.iter().map(|&index| index as usize).collect(), true)
}
fn copied(&self, at: Vec<usize>, constants_stay: bool) -> Result<Self> {
let rows = at.len();
let (at, leaf) = self.resolve(at);
let live: Vec<bool> = at.iter().map(|&index| index != NOWHERE).collect();
let validity = Validity::from_run(&live);
let body = match &leaf.body {
Body::Constant(value) => {
if constants_stay && matches!(validity, Validity::AllValid) {
return Ok(Self::constant(self.ty.clone(), value.as_ref().clone(), rows));
}
let mut data = empty_data_for(&self.ty)?;
for &index in &at {
push_value(&mut data, if index == NOWHERE { &Value::Null } else { value })?;
}
Body::Flat(data)
}
Body::Sequence { start, step } => Body::Flat(Data::Int64(
at.iter()
.map(|&index| if index == NOWHERE { 0 } else { start + step * index as i64 })
.collect(),
)),
Body::Flat(Data::Empty) => {
return Ok(Self::constant(self.ty.clone(), Value::Null, rows));
}
Body::Flat(data) => Body::Flat(copy_of(data, &at)),
Body::Dictionary { .. } => {
return Err(Error::internal("a dictionary survived being resolved"));
}
};
Ok(Self { ty: self.ty.clone(), len: rows, validity, body })
}
fn resolve(&self, mut at: Vec<usize>) -> (Vec<usize>, &Self) {
let mut source = self;
loop {
for slot in &mut at {
if *slot >= source.len || !source.validity.is_valid(*slot) {
*slot = NOWHERE;
}
}
let Body::Dictionary { codes, values } = &source.body else {
return (at, source);
};
for slot in &mut at {
*slot = match codes.get(*slot) {
Some(&code) => code as usize,
None => NOWHERE,
};
}
source = values.as_ref();
}
}
}
fn compose(codes: Vec<u32>, values: Vector) -> (Vec<u32>, Vector) {
if !matches!(values.validity, Validity::AllValid) {
return (codes, values);
}
let Vector { ty, len, validity, body } = values;
match body {
Body::Dictionary { codes: inner, values: leaf } => {
debug_assert!(
!matches!(leaf.body, Body::Dictionary { .. })
|| !matches!(leaf.validity, Validity::AllValid),
"a dictionary was stacked on a dictionary without going through the constructor"
);
(codes.iter().map(|&code| inner[code as usize]).collect(), *leaf)
}
body => (codes, Vector { ty, len, validity, body }),
}
}
const NOWHERE: usize = usize::MAX;
fn copy_of(data: &Data, at: &[usize]) -> Data {
macro_rules! copied {
($(($variant:ident, $native:ty, $zero:expr)),+ $(,)?) => {
match data {
Data::Empty => Data::Empty,
$(Data::$variant(values) => {
let mut out = Buffer::with_capacity(at.len());
for &index in at {
out.push(values.get(index).copied().unwrap_or($zero));
}
Data::$variant(out)
})+
Data::Varlen(values) => {
let mut out = StringColumn::with_capacity(at.len());
let views = values.views();
out.reserve_bytes(
at.iter()
.filter_map(|&index| views.get(index))
.filter(|view| !view.is_inline())
.map(StringView::len)
.sum(),
);
for &index in at {
out.push(values.get(index).unwrap_or(""));
}
Data::Varlen(out)
}
}
};
}
crate::for_each_layout!(fixed, copied)
}
fn layout_of(data: &Data) -> rudb_common::PhysicalType {
use rudb_common::PhysicalType as P;
macro_rules! layouts {
($(($variant:ident, $native:ty, $zero:expr)),+ $(,)?) => {
match data {
Data::Empty => P::Empty,
$(Data::$variant(_) => P::$variant,)+
}
};
}
crate::for_each_layout!(all, layouts)
}
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;
macro_rules! empties {
($(($variant:ident, $native:ty, $zero:expr)),+ $(,)?) => {
match ty.physical() {
P::Empty => Data::Empty,
$(P::$variant => Data::$variant(Buffer::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"
)));
}
}
};
}
Ok(crate::for_each_layout!(fixed, empties))
}
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"
)));
}
}
};
}
macro_rules! decimal {
($vec:expr, $ty:ty, $unscaled:expr) => {
match <$ty>::try_from(*$unscaled) {
Ok(x) => $vec.push(x),
Err(_) => {
return Err(Error::internal(format!(
"an unscaled decimal of {} does not fit the run its precision chose",
$unscaled
)));
}
}
};
}
match data {
Data::Empty => {}
Data::Bool(v) => push!(v, Value::Boolean, false),
Data::Int8(v) => push!(v, Value::TinyInt, 0),
Data::Int16(v) => match value {
Value::Null => v.push(0),
Value::SmallInt(x) => v.push(*x),
Value::Decimal { unscaled, .. } => decimal!(v, i16, unscaled),
other => return Err(Error::internal(format!("{other:?} is not a 16 bit value"))),
},
Data::Int32(v) => match value {
Value::Null => v.push(0),
Value::Integer(x) | Value::Date(x) => v.push(*x),
Value::Decimal { unscaled, .. } => decimal!(v, i32, unscaled),
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),
Value::Decimal { unscaled, .. } => decimal!(v, i64, unscaled),
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().into())).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_vector_built_from_values_reads_the_same_values_back() {
let vector = Vector::from_values(
LogicalType::Varchar,
&[
Value::Varchar("a".to_string()),
Value::Null,
Value::Varchar("a string too long to sit inside a view".to_string()),
],
)
.expect("strings and a null");
assert_eq!(vector.len(), 3);
assert_eq!(vector.value_at(0), Value::Varchar("a".to_string()));
assert_eq!(vector.value_at(1), Value::Null);
assert_eq!(
vector.value_at(2),
Value::Varchar("a string too long to sit inside a view".to_string())
);
}
#[test]
fn a_null_in_the_middle_does_not_move_the_values_after_it() {
let vector = Vector::from_values(
LogicalType::Integer,
&[Value::Integer(1), Value::Null, Value::Integer(3)],
)
.expect("integers and a null");
assert_eq!(vector.value_at(2), Value::Integer(3));
assert!(vector.validity().has_nulls(3), "the middle one is null");
}
#[test]
fn a_value_the_type_cannot_hold_is_refused() {
let wrong = Vector::from_values(LogicalType::Integer, &[Value::Varchar("x".to_string())]);
assert!(wrong.is_err(), "a string is not an integer");
}
#[test]
fn a_type_that_does_not_match_its_layout_is_refused_at_construction() {
let wrong = Vector::flat(LogicalType::Varchar, Data::Int32(vec![1].into()));
assert!(wrong.is_err());
let right = Vector::flat(LogicalType::Date, Data::Int32(vec![1].into()));
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 a_null_behind_a_dictionary_survives_flattening() {
let values =
Vector::from_values(LogicalType::Integer, &[Value::Integer(3), Value::Null]).unwrap();
let dictionary = Vector::dictionary(vec![1, 0, 1], values).unwrap();
let flat = dictionary.flatten().unwrap();
assert_eq!(flat.value_at(0), Value::Null);
assert_eq!(flat.value_at(1), Value::Integer(3));
assert_eq!(flat.value_at(2), Value::Null);
}
#[test]
fn gathering_reads_what_reading_one_position_at_a_time_reads() {
let mut column = StringColumn::new();
column.push("alpha");
column.push("beta");
column.push("gamma");
let cases = [
integers(&[10, 20, 30, 40]),
integers(&[10, 20, 30, 40]).with_validity(Validity::from_iter(4, |i| i != 2)),
Vector::constant(LogicalType::Integer, Value::Integer(9), 4),
Vector::sequence(100, -7, 4),
Vector::sequence(100, -7, 4).with_validity(Validity::from_iter(4, |i| i % 2 == 0)),
Vector::dictionary(
vec![2, 0, 1, 2],
Vector::flat(LogicalType::Varchar, Data::Varlen(column)).unwrap(),
)
.unwrap(),
Vector::dictionary(
vec![1, 0, 1, 0],
Vector::from_values(LogicalType::Integer, &[Value::Integer(5), Value::Null])
.unwrap(),
)
.unwrap(),
];
let wanted = [3_u32, 0, 2, 2, 1];
for vector in cases {
let gathered = vector.gather(&wanted).unwrap();
assert_eq!(gathered.len(), wanted.len());
assert_eq!(gathered.logical_type(), vector.logical_type());
for (slot, &index) in wanted.iter().enumerate() {
assert_eq!(
gathered.value_at(slot),
vector.value_at(index as usize),
"slot {slot} of {:?}",
vector.form()
);
}
}
}
#[test]
fn gathering_a_position_that_is_not_there_is_a_null_and_not_a_wrong_value() {
let vector = integers(&[1, 2, 3]);
let gathered = vector.gather(&[2, 9]).unwrap();
assert_eq!(gathered.value_at(0), Value::Integer(3));
assert_eq!(gathered.value_at(1), Value::Null);
}
#[test]
fn gathering_from_a_vector_of_no_values_is_that_many_nulls() {
let vector = Vector::flat(LogicalType::Null, Data::Empty).unwrap();
let gathered = vector.gather(&[0, 1, 2]).unwrap();
assert_eq!(gathered.len(), 3);
assert_eq!(gathered.value_at(0), Value::Null);
assert_eq!(gathered.value_at(2), Value::Null);
}
#[test]
fn gathering_a_constant_stays_a_constant() {
let vector = Vector::constant(LogicalType::Integer, Value::Integer(4), 100);
let gathered = vector.gather(&[7, 7, 99]).unwrap();
assert_eq!(gathered.form(), Form::Constant);
assert_eq!(gathered.len(), 3);
assert_eq!(gathered.value_at(2), Value::Integer(4));
}
#[test]
fn gathering_walks_a_dictionary_over_a_dictionary_to_the_values() {
let inner = Vector::dictionary(vec![2, 1, 0], integers(&[7, 8, 9]))
.unwrap()
.with_validity(Validity::from_iter(3, |index| index != 2));
let outer = Vector::dictionary(vec![1, 2], inner).unwrap();
let gathered = outer.gather(&[0, 1]).unwrap();
assert_eq!(gathered.form(), Form::Flat);
assert_eq!(gathered.value_at(0), Value::Integer(8));
assert_eq!(gathered.value_at(1), Value::Null);
}
#[test]
fn a_dictionary_over_a_dictionary_is_composed_into_one_level() {
let inner = Vector::dictionary(vec![2, 1, 0], integers(&[7, 8, 9])).unwrap();
let outer = Vector::dictionary(vec![1, 2], inner).unwrap();
let (codes, values) = outer.dictionary_parts().unwrap();
assert_eq!(codes, [1, 0]);
assert_eq!(values.form(), Form::Flat);
assert_eq!(outer.value_at(0), Value::Integer(8));
assert_eq!(outer.value_at(1), Value::Integer(7));
}
#[test]
fn stacking_dictionaries_does_not_make_them_deeper() {
let mut vector = integers(&[10, 20, 30, 40]);
for _ in 0..4 {
vector = Vector::dictionary(vec![3, 2, 1, 0], vector).unwrap();
}
let (codes, values) = vector.dictionary_parts().unwrap();
assert_eq!(values.form(), Form::Flat);
assert_eq!(codes, [0, 1, 2, 3]);
assert_eq!(
vector.iter().collect::<Vec<_>>(),
integers(&[10, 20, 30, 40]).iter().collect::<Vec<_>>()
);
}
#[test]
fn composing_a_dictionary_keeps_the_nulls_its_values_hold() {
let values =
Vector::from_values(LogicalType::Integer, &[Value::Integer(3), Value::Null]).unwrap();
let inner = Vector::dictionary(vec![1, 0, 1], values).unwrap();
let outer = Vector::dictionary(vec![0, 1], inner).unwrap();
assert_eq!(outer.dictionary_parts().unwrap().1.form(), Form::Flat);
assert_eq!(outer.value_at(0), Value::Null);
assert_eq!(outer.value_at(1), Value::Integer(3));
}
#[test]
fn a_dictionary_holding_its_own_nulls_is_not_composed_past() {
let inner = Vector::dictionary(vec![0, 1, 2], integers(&[1, 2, 3]))
.unwrap()
.with_validity(Validity::from_iter(3, |index| index != 1));
let outer = Vector::dictionary(vec![1, 2, 0], inner).unwrap();
assert_eq!(outer.dictionary_parts().unwrap().1.form(), Form::Dictionary);
assert_eq!(outer.value_at(0), Value::Null);
assert_eq!(outer.value_at(1), Value::Integer(3));
assert_eq!(outer.value_at(2), Value::Integer(1));
}
#[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].into())).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");
}
#[test]
fn a_decimal_writes_into_whichever_of_the_four_runs_its_precision_chose() {
for (width, scale, unscaled) in
[(4u8, 1u8, 25i128), (9, 2, 1234), (18, 3, 123_456), (38, 4, 1_234_567)]
{
let ty = LogicalType::decimal(width, scale).unwrap();
let value = Value::Decimal { unscaled, width, scale };
let vector = Vector::from_values(ty, &[value.clone(), Value::Null]).unwrap();
assert_eq!(vector.value_at(0), value, "a decimal of width {width}");
assert_eq!(vector.value_at(1), Value::Null, "a null decimal of width {width}");
}
}
#[test]
fn a_decimal_too_wide_for_the_run_its_type_chose_is_an_error_and_not_a_wrong_number() {
let ty = LogicalType::decimal(4, 1).unwrap();
let value = Value::Decimal { unscaled: 1_000_000, width: 4, scale: 1 };
let error = Vector::from_values(ty, &[value]).unwrap_err();
assert!(error.to_string().contains("does not fit"), "{error}");
}
}