Skip to main content

Vector

Struct Vector 

Source
pub struct Vector { /* private fields */ }
Expand description

A type, a length, a validity representation and some data.

Implementations§

Source§

impl Vector

Source

pub fn flat(ty: LogicalType, data: Data) -> Result<Self>

A flat vector of data, all valid.

§Errors

If the data’s physical layout is not the one the type calls for. That check is here rather than left to the caller because a vector whose type and layout disagree is a wrong answer waiting to be read out, and it costs one comparison at construction to prevent.

Source

pub fn from_values(ty: LogicalType, values: &[Value]) -> Result<Self>

A flat vector built from single values, with the nulls among them turning into validity.

The slow way in, and the only way in that anything outside this crate has. It is what an INSERT, a VALUES clause and a test build a column with, all of which arrive holding values rather than a run of i32. Nothing on a scan path calls it: a scan produces a run of data directly and hands it to Self::flat.

§Errors

If a value is not one the type can hold, or if the type is one that cannot be stored flat yet, which today means the nested types.

Source

pub fn constant(ty: LogicalType, value: Value, len: usize) -> Self

A vector of len copies of one value.

Costs one value regardless of the length, which is what makes a literal in a predicate free and what makes a projection of a constant free.

Source

pub fn sequence(start: i64, step: i64, len: usize) -> Self

A vector of len values starting at start and stepping by step.

This is what a row identifier column is, and it costs sixteen bytes rather than eight kilobytes. A scan that produces row ids for a later fetch produces one of these.

Source

pub fn dictionary(codes: Vec<u32>, values: Vector) -> Result<Self>

A vector of codes into a smaller vector of distinct values.

The form the whole M3 thesis rests on. A dictionary vector handed to a group by is an integer column, and an aggregate over one is an aggregate over integers no matter what the logical type says.

A dictionary over a dictionary is composed into one level here rather than left as two, so the form has a depth of one always and a kernel that reads Self::dictionary_parts is reading the values rather than another layer of codes. Two filters over the same chunk build the second case and four conjuncts pushed down separately build four of it.

The cost of leaving them stacked turned out to be a cliff rather than a slope. Every loop in rudb-kernels reaches for the values behind the codes with Self::data, a dictionary pointing at a dictionary has no data to hand back, so the second level does not make the kernels slower, it turns them off and drops the work onto the row at a time path that exists to be correct rather than fast. Measured on server3 over a chunk of two numeric columns and a consumer of two vectorized passes, one level reads at 3.5 nanoseconds a row and two levels at 104, and the third and fourth levels cost almost nothing more because the first one had already given up everything there was to give. Composing is one pass over the outer codes, which the range check above is already making.

The one dictionary that is not composed past is one carrying a validity of its own. A dictionary is built all valid and only Self::with_validity can change that, so such a vector is saying that its nulls are at this level rather than in the values it points at, and composing past it would drop them.

§Errors

If any code is past the end of the value vector.

Source

pub fn with_validity(self, validity: Validity) -> Self

The same vector with a different validity.

Source

pub fn logical_type(&self) -> &LogicalType

What kind of values these are.

Source

pub fn len(&self) -> usize

How many values there are.

Source

pub fn is_empty(&self) -> bool

Whether there are no values.

Source

pub fn footprint(&self) -> usize

How many bytes of memory this vector is holding.

What the memory limit charges for it. A constant and a sequence hold one value and two numbers however long they are, which is the point of both forms, so the number here is the form’s cost and not the column’s width times its length.

A dictionary counts its values in full, and two vectors sharing one dictionary each report all of it. That over counts, deliberately: working out that two operators are looking at the same Arc means threading identity through the accounting, and a limit that over counts refuses a query that would have fit while a limit that under counts lets one through that does not. The first is a worse answer to give and the second is a worse thing to be.

Source

pub fn validity(&self) -> &Validity

Which of the values are not null.

Source

pub fn form(&self) -> Form

Which physical form this vector is in.

Source

pub fn data(&self) -> Option<&Data>

The data, for a flat vector, and None for any other form.

A kernel that wants a slice asks for it and takes the flat path if it gets one. A kernel that can do better on a constant or a dictionary checks Self::form first.

Source

pub fn constant_value(&self) -> Option<&Value>

The one value, for a constant vector, and None for any other form.

A kernel comparing a column against a literal wants the literal once rather than 1024 times, and Self::value_at on a constant clones it on every call because it has to be able to hand back a Value for any form. This is the accessor that lets the specialized path hoist the clone out of the loop.

Source

pub fn dictionary_parts(&self) -> Option<(&[u32], &Self)>

The codes and the values, for a dictionary vector, and None for any other form.

The reason a kernel needs this rather than reading the dictionary through Self::value_at is the entire argument for the form existing. A filter against a dictionary column of 1024 rows and 40 distinct values is 40 comparisons and 1024 lookups, not 1024 comparisons, and there is no way to write that loop without seeing the codes.

Note what the validity of the returned vector means. A dictionary keeps its nulls in the vector it points at, and the dictionary’s own validity says nothing about them, so a caller deciding whether row i is null has to ask the value vector about codes[i] rather than asking this vector about i. Self::flatten has the same note on it for the same reason, because getting this wrong is a null that survives being selected and comes out as a zero.

Source

pub fn sequence_parts(&self) -> Option<(i64, i64)>

The start and the step, for a sequence vector, and None for any other form.

Source

pub fn value_at(&self, index: usize) -> Value

The value at index, as a single value.

This is the slow path on purpose. It is what a result set is read out with and what a test asserts on, and an operator that calls it per row is an operator that has already lost the argument the vector interface exists to win.

Source

pub fn iter(&self) -> impl Iterator<Item = Value> + '_

Every value in order, as single values.

Source

pub fn slice(&self, at: usize, len: usize) -> Result<Self>

A contiguous run of the values, in the form they are already in.

This is the cut Self::gather cannot do. A gather walks a dictionary to its leaf and copies, so gathering a piece of a dictionary encoded column hands back a flat one, and a caller that only wanted the first thousand rows of a page has silently paid for a copy and thrown the dictionary away. A group by over a dictionary encoded column is the case that cares, and it is most of ClickBench.

So each form is cut as itself. A dictionary keeps its dictionary and slices its codes, a sequence stays arithmetic with its start moved along, a constant stays a shorter constant, and a flat body is the one that genuinely has to copy its range.

The dictionary itself is shared rather than copied, so a cut is the codes and nothing else. It used to be copied, and on a read of a ClickBench partition that copy was ten percent of the cycles: a page holds one dictionary and is cut into chunk sized pieces, so the whole dictionary was copied once per chunk to be read the same way each time.

§Errors

If the range runs past the end of the vector, or if the type has no flat layout and the body is one that has to be copied.

Source

pub fn flatten(&self) -> Result<Self>

The same values in flat form.

Flattening a vector that is already flat is free. Flattening any other form costs a copy, which is exactly why the other forms exist and why nothing on the hot path should call this. It is here for the operators that genuinely cannot do better and for the tests that check the other forms against it.

§Errors

If the type is one this crate cannot store flat yet, which today means the nested types.

Source

pub fn gather(&self, indices: &[u32]) -> Result<Self>

The values at the given positions, copied, in a form that does not point back at this vector.

This is the copying counterpart to Self::dictionary, and the two are the two halves of the decision spec/07-execution.md section 7.1 describes. Which half is right is measured rather than argued, and Chunk::compact is where the measurement is written down.

A dictionary chain is walked to its leaf first and the codes composed on the way down, so the copy runs once over the data rather than once per level, and a position that is null at any level comes out null here. The copy is a typed loop per physical layout rather than a Value per row, which is the whole point of it and is what Self::flatten now goes through too.

§Errors

If the type has no flat layout, which today means the nested types.

Trait Implementations§

Source§

impl AsRef<Vector> for Vector

So that a kernel can take its operands as either a list of vectors or a list of references.

A caller that built a Vec<Vector> and a caller whose operands are already somewhere else, in a chunk or in an evaluator’s scratch, want the same kernel. Without this the second kind has to clone every operand into a Vec to satisfy the signature, and a clone of a vector is a copy of the whole column, so the type would be charging real memory traffic for nothing.

Source§

fn as_ref(&self) -> &Vector

Converts this type into a shared reference of the (usually inferred) input type.
Source§

impl Clone for Vector

Source§

fn clone(&self) -> Vector

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Vector

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl PartialEq for Vector

Source§

fn eq(&self, other: &Vector) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
Source§

impl StructuralPartialEq for Vector

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.