mod alive;
mod bitmap;
#[cfg(any(test, feature = "test-support"))]
#[doc(hidden)]
pub mod bitmap_observer;
mod columns;
mod dict;
mod predicate;
pub use alive::{AliveError, AliveSet};
pub use bitmap::DocBitmap;
#[cfg(any(test, feature = "test-support"))]
#[doc(hidden)]
pub use columns::MetadataBuildTestLimits;
pub use columns::{
BoolColumn, BuildError, Column, ColumnInput, ColumnStore, ColumnStoreBuilder, ColumnValue,
DictionaryColumn, NumericColumn, RawStringColumn,
};
pub use dict::{CodeWidth, DictionaryCodes, DictionaryError, StringDictionary};
pub use predicate::{EvalError, Predicate, PredicateValue, RangeBound, RangePredicate, evaluate};
pub const TIMESTAMP_COLUMN: ColumnId = ColumnId(0);
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[repr(transparent)]
pub struct ColumnId(u32);
impl ColumnId {
#[must_use]
pub const fn new(value: u32) -> Self {
Self(value)
}
#[must_use]
pub const fn get(self) -> u32 {
self.0
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ColumnType {
U64,
I64,
F64,
Bool,
DictionaryString,
RawString,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ColumnDefinition {
id: ColumnId,
name: String,
column_type: ColumnType,
nullable: bool,
}
impl ColumnDefinition {
#[must_use]
pub fn new(
id: ColumnId,
name: impl Into<String>,
column_type: ColumnType,
nullable: bool,
) -> Self {
Self {
id,
name: name.into(),
column_type,
nullable,
}
}
#[must_use]
pub const fn id(&self) -> ColumnId {
self.id
}
#[must_use]
pub fn name(&self) -> &str {
&self.name
}
#[must_use]
pub const fn column_type(&self) -> ColumnType {
self.column_type
}
#[must_use]
pub const fn is_nullable(&self) -> bool {
self.nullable
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum SchemaError {
ReservedTimestampId,
ReservedTimestampName,
DuplicateColumnId(ColumnId),
DuplicateColumnName(String),
}
impl std::fmt::Display for SchemaError {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::ReservedTimestampId => formatter.write_str("column id 0 is reserved for ts"),
Self::ReservedTimestampName => formatter.write_str("column name ts is reserved"),
Self::DuplicateColumnId(id) => write!(formatter, "duplicate column id {}", id.get()),
Self::DuplicateColumnName(name) => write!(formatter, "duplicate column name {name}"),
}
}
}
impl std::error::Error for SchemaError {}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Schema {
columns: Vec<ColumnDefinition>,
}
impl Schema {
pub(crate) fn resident_bytes(&self) -> Option<usize> {
let definitions = self
.columns
.capacity()
.checked_mul(std::mem::size_of::<ColumnDefinition>())?;
self.columns
.iter()
.try_fold(definitions, |total, definition| {
total.checked_add(definition.name.capacity())
})
}
#[must_use]
pub fn timestamp_only() -> Self {
Self {
columns: vec![ColumnDefinition::new(
TIMESTAMP_COLUMN,
"ts",
ColumnType::I64,
false,
)],
}
}
pub fn new(user_columns: Vec<ColumnDefinition>) -> Result<Self, SchemaError> {
for (position, definition) in user_columns.iter().enumerate() {
if definition.id == TIMESTAMP_COLUMN {
return Err(SchemaError::ReservedTimestampId);
}
if definition.name == "ts" {
return Err(SchemaError::ReservedTimestampName);
}
if let Some(duplicate) = user_columns.iter().take(position).find(|candidate| {
candidate.id == definition.id || candidate.name == definition.name
}) {
if duplicate.id == definition.id {
return Err(SchemaError::DuplicateColumnId(definition.id));
}
return Err(SchemaError::DuplicateColumnName(definition.name.clone()));
}
}
let mut columns = Self::timestamp_only().columns;
columns.reserve(user_columns.len());
columns.extend(user_columns);
Ok(Self { columns })
}
#[must_use]
pub fn columns(&self) -> &[ColumnDefinition] {
&self.columns
}
#[must_use]
pub fn column(&self, id: ColumnId) -> Option<&ColumnDefinition> {
self.columns.iter().find(|definition| definition.id == id)
}
#[must_use]
pub fn user_column_count(&self) -> usize {
self.columns.len().saturating_sub(1)
}
}