Rename column_position ~> column variables, and introduce runtime error AttemptToIndexAlreadyIndexedColumn

This commit is contained in:
Yuriy Dupyn 2024-01-28 15:27:03 +01:00
parent 052236d892
commit 12c91ce70e
5 changed files with 71 additions and 38 deletions

View file

@ -10,6 +10,8 @@ pub enum RuntimeError {
AttemptingToInsertAlreadyPresentId(TableName, Uuid),
#[error("table {0} cannot be indexed on column {1}")]
AttemptToIndexNonIndexableColumn(TableName, ColumnName),
#[error("table {0} already indexes column {1}")]
AttemptToIndexAlreadyIndexedColumn(TableName, ColumnName),
}
#[derive(Debug, Error)]

View file

@ -60,16 +60,16 @@ impl Row {
self.0.len()
}
pub fn get(&self, column_position: ColumnPosition) -> Option<&Value> {
self.0.get(column_position)
pub fn get(&self, column: ColumnPosition) -> Option<&Value> {
self.0.get(column)
}
pub fn restrict_columns(&self, columns: &Vec<ColumnPosition>) -> RestrictedRow {
// If the index from `columns` is non-existant in `row`, it will just ignore it.
let mut subrow: Vec<(ColumnPosition, Value)> = vec![];
for column_position in columns {
if let Some(value) = self.get(*column_position) {
subrow.push((*column_position, value.clone()));
for column in columns {
if let Some(value) = self.get(*column) {
subrow.push((*column, value.clone()));
}
}

View file

@ -54,12 +54,12 @@ impl Table {
.collect()
}
fn get_rows_by_value(&self, column_position: ColumnPosition, value: &Value) -> Vec<Row> {
fn get_rows_by_value(&self, column: ColumnPosition, value: &Value) -> Vec<Row> {
// brute-force search
self.rows
.values()
.filter_map(|row| {
if row.get(column_position) == Some(value) {
if row.get(column) == Some(value) {
Some(row.clone())
} else {
None
@ -68,21 +68,21 @@ impl Table {
.collect()
}
pub fn select_all_rows<'a>(&'a self, selected_column_positions: Vec<ColumnPosition>) -> impl Iterator<Item=RestrictedRow> + 'a {
pub fn select_all_rows<'a>(&'a self, selected_columns: Vec<ColumnPosition>) -> impl Iterator<Item=RestrictedRow> + 'a {
self.rows
.values()
.map(move |row| row.restrict_columns(&selected_column_positions))
.map(move |row| row.restrict_columns(&selected_columns))
}
pub fn select_rows_where_eq<'a>(
&'a self,
selected_column_positions: Vec<ColumnPosition>,
column_position: ColumnPosition,
selected_columns: Vec<ColumnPosition>,
column: ColumnPosition,
value: Value,
) -> DbResult<impl Iterator<Item=RestrictedRow> + 'a> {
let restrict_columns_of_row = move |row: Row| row.restrict_columns(&selected_column_positions);
let restrict_columns_of_row = move |row: Row| row.restrict_columns(&selected_columns);
match value {
Value::Indexable(value) => match self.fetch_ids_from_index(column_position, &value)? {
Value::Indexable(value) => match self.fetch_ids_from_index(column, &value)? {
Some(ids) =>
Ok(self
.get_rows_by_ids(ids)
@ -91,14 +91,14 @@ impl Table {
),
None =>
Ok(self
.get_rows_by_value(column_position, &Value::Indexable(value))
.get_rows_by_value(column, &Value::Indexable(value))
.into_iter()
.map(restrict_columns_of_row)
),
},
_ =>
Ok(self
.get_rows_by_value(column_position, &value)
.get_rows_by_value(column, &value)
.into_iter()
.map(restrict_columns_of_row)
),
@ -129,8 +129,8 @@ impl Table {
fn delete_row_by_id(&mut self, id: Uuid) -> usize {
match self.rows.remove(&id) {
Some(row) => {
for (column_position, column_index) in &mut self.indexes {
if let Value::Indexable(value) = &row[*column_position] {
for (column, column_index) in &mut self.indexes {
if let Value::Indexable(value) = &row[*column] {
let _ = column_index.remove(value, id);
};
}
@ -148,12 +148,12 @@ impl Table {
total_count
}
fn delete_rows_by_value(&mut self, column_position: ColumnPosition, value: &Value) -> usize {
fn delete_rows_by_value(&mut self, column: ColumnPosition, value: &Value) -> usize {
let matched_ids: HashSet<Uuid> = self
.rows
.iter()
.filter_map(|(id, row)| {
if row.get(column_position) == Some(value) {
if row.get(column) == Some(value) {
Some(*id)
} else {
None
@ -172,38 +172,43 @@ impl Table {
pub fn delete_rows_where_eq(
&mut self,
column_position: ColumnPosition,
column: ColumnPosition,
value: Value,
) -> DbResult<usize> {
match value {
Value::Indexable(value) => match self.fetch_ids_from_index(column_position, &value)? {
Value::Indexable(value) => match self.fetch_ids_from_index(column, &value)? {
Some(ids) => Ok(self.delete_rows_by_ids(ids)),
None => Ok(self.delete_rows_by_value(column_position, &Value::Indexable(value))),
None => Ok(self.delete_rows_by_value(column, &Value::Indexable(value))),
},
_ => Ok(self.delete_rows_by_value(column_position, &value)),
_ => Ok(self.delete_rows_by_value(column, &value)),
}
}
// ======Indexing======
pub fn attach_index(&mut self, column_position: ColumnPosition) -> DbResult<()> {
pub fn attach_index(&mut self, column: ColumnPosition) -> DbResult<()> {
if self.indexes.get(&column).is_some() {
let column_name = self.schema.column_name_from_column(column).clone();
let table_name = self.schema.table_name().clone();
return Err(RuntimeError::AttemptToIndexAlreadyIndexedColumn(table_name, column_name))
}
let mut column_index: ColumnIndex = ColumnIndex::new();
update_index_from_table(&mut column_index, self, column_position)?;
self.indexes.insert(column_position, column_index);
update_index_from_table(&mut column_index, self, column)?;
self.indexes.insert(column, column_index);
Ok(())
}
fn fetch_ids_from_index(
&self,
column_position: ColumnPosition,
column: ColumnPosition,
value: &IndexableValue,
) -> DbResult<Option<HashSet<Uuid>>> {
if self.schema.is_primary(column_position) {
if self.schema.is_primary(column) {
match value {
IndexableValue::Uuid(id) => Ok(Some(HashSet::from([*id]))),
_ => unreachable!() // SAFETY: Validation guarantees primary column has correct Uuid type.
}
} else {
match self.indexes.get(&column_position) {
match self.indexes.get(&column) {
Some(index) => {
// Note that we are cloning the ids here! This can be very wasteful in some cases.
// Theoretically it would be possible to return a reference,

View file

@ -20,8 +20,8 @@ pub type ColumnName = String;
impl TableSchema {
pub fn new(table_name: TableName, primary_key: ColumnPosition, column_name_position_map: Vec<(ColumnName, ColumnPosition)>, types: Vec<DbType>) -> Self {
let mut column_name_position_mapping: BiMap<ColumnName, ColumnPosition> = BiMap::new();
for (column_name, column_position) in column_name_position_map {
column_name_position_mapping.insert(column_name, column_position);
for (column_name, column) in column_name_position_map {
column_name_position_mapping.insert(column_name, column);
}
Self { table_name, primary_key, column_name_position_mapping, types }
}
@ -30,8 +30,8 @@ impl TableSchema {
&self.table_name
}
pub fn column_type(&self, column_position: ColumnPosition) -> DbType {
self.types[column_position]
pub fn column_type(&self, column: ColumnPosition) -> DbType {
self.types[column]
}
pub fn get_columns(&self) -> Vec<&ColumnName> {
@ -62,8 +62,8 @@ impl TableSchema {
self.types.get(position).copied()
}
pub fn is_primary(&self, column_position: ColumnPosition) -> bool {
self.primary_key == column_position
pub fn is_primary(&self, column: ColumnPosition) -> bool {
self.primary_key == column
}
// Assumes `column` comes from a validated source.