use rustc_hash::FxHashMap;
use radixdb_catalog::CatalogMutationSet;
use radixdb_core::{Error, IndexType, IsolationLevel, Result, Schema, SchemaColumn};
use crate::expression::Expression;
use crate::index::PartialIndexPredicate;
use crate::traits::{QueryResult, Table};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PendingIndexDefinition {
pub table_name: String,
pub index_name: String,
pub columns: Vec<String>,
pub is_unique: bool,
pub index_type: Option<IndexType>,
pub hnsw_m: Option<u16>,
pub hnsw_ef_construction: Option<u16>,
pub hnsw_ef_search: Option<u16>,
pub hnsw_distance_metric: Option<u8>,
pub partial_predicate: Option<PartialIndexPredicate>,
pub key_encoder: Option<crate::index::PreparedIndexKeyEncoder>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PendingIndexDrop {
pub table_name: String,
pub index_name: String,
pub schema_owned: bool,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PendingIndexRename {
pub table_name: String,
pub old_index_name: String,
pub new_index_name: String,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PendingTableRename {
pub old_name: String,
pub new_name: String,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PendingSchemaChange {
pub table_name: String,
pub schema: Schema,
pub expected_catalog_schema: Schema,
pub requires_row_normalization: bool,
pub physical_transition: Option<SchemaPhysicalTransition>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SchemaPhysicalTransition {
DropColumn {
column_name: String,
column_index: usize,
},
RenameColumn {
old_name: String,
new_name: String,
},
}
pub trait Transaction: Send {
fn is_active(&self) -> bool {
true
}
fn begin(&mut self) -> Result<()>;
fn commit(&mut self) -> Result<()>;
fn rollback(&mut self) -> Result<()>;
fn create_savepoint(&mut self, name: &str) -> Result<()>;
fn release_savepoint(&mut self, name: &str) -> Result<()>;
fn rollback_to_savepoint(&mut self, name: &str) -> Result<()>;
fn stage_catalog_mutation(&mut self, _mutation: CatalogMutationSet) -> Result<()> {
Err(Error::NotSupported(
"transactional catalog mutation is not supported by this storage engine".to_string(),
))
}
fn get_savepoint_timestamp(&self, name: &str) -> Option<i64>;
fn id(&self) -> i64;
fn set_isolation_level(&mut self, level: IsolationLevel) -> Result<()>;
fn create_table(&mut self, name: &str, schema: Schema) -> Result<Box<dyn Table>>;
fn drop_table(&mut self, name: &str) -> Result<()>;
fn get_table(&self, name: &str) -> Result<Box<dyn Table>>;
fn list_tables(&self) -> Result<Vec<String>>;
fn rename_table(&mut self, old_name: &str, new_name: &str) -> Result<()>;
fn create_table_index(
&mut self,
table_name: &str,
index_name: &str,
columns: &[String],
is_unique: bool,
) -> Result<()>;
fn stage_create_index(&mut self, _definition: PendingIndexDefinition) -> Result<()> {
Err(Error::NotSupported(
"transactional CREATE INDEX is not supported by this storage engine".to_string(),
))
}
fn staged_index_definitions(&self, _table_name: &str) -> Vec<PendingIndexDefinition> {
Vec::new()
}
fn stage_drop_index(&mut self, _drop: PendingIndexDrop) -> Result<()> {
Err(Error::NotSupported(
"transactional DROP INDEX is not supported by this storage engine".to_string(),
))
}
fn stage_rename_index(&mut self, _rename: PendingIndexRename) -> Result<()> {
Err(Error::NotSupported(
"transactional ALTER INDEX is not supported by this storage engine".to_string(),
))
}
fn drop_table_index(&mut self, table_name: &str, index_name: &str) -> Result<()>;
fn create_table_btree_index(
&mut self,
table_name: &str,
column_name: &str,
is_unique: bool,
custom_name: Option<&str>,
) -> Result<()>;
fn drop_table_btree_index(&mut self, table_name: &str, column_name: &str) -> Result<()>;
fn add_table_column(&mut self, table_name: &str, column: SchemaColumn) -> Result<()>;
fn stage_table_schema_change(
&mut self,
_table_name: &str,
_schema: Schema,
_requires_row_normalization: bool,
) -> Result<()> {
Err(Error::NotSupported(
"transactional ALTER TABLE schema replacement is not supported by this storage engine"
.to_string(),
))
}
fn drop_table_column(&mut self, table_name: &str, column_name: &str) -> Result<()>;
fn rename_table_column(
&mut self,
table_name: &str,
old_name: &str,
new_name: &str,
) -> Result<()>;
fn stage_table_schema_transition(
&mut self,
table_name: &str,
schema: Schema,
requires_row_normalization: bool,
transition: SchemaPhysicalTransition,
) -> Result<()>;
fn modify_table_column(&mut self, table_name: &str, column: SchemaColumn) -> Result<()>;
fn select(
&self,
table_name: &str,
columns_to_fetch: &[String],
expr: Option<&dyn Expression>,
original_columns: Option<&[String]>,
) -> Result<Box<dyn QueryResult>>;
fn select_with_aliases(
&self,
table_name: &str,
columns_to_fetch: &[String],
expr: Option<&dyn Expression>,
aliases: &FxHashMap<String, String>,
original_columns: Option<&[String]>,
) -> Result<Box<dyn QueryResult>>;
fn select_as_of(
&self,
table_name: &str,
columns_to_fetch: &[String],
expr: Option<&dyn Expression>,
temporal_type: &str,
temporal_value: i64,
original_columns: Option<&[String]>,
) -> Result<Box<dyn QueryResult>>;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TemporalType {
Transaction,
Timestamp,
}
impl std::str::FromStr for TemporalType {
type Err = ();
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
match s.to_uppercase().as_str() {
"TRANSACTION" => Ok(Self::Transaction),
"TIMESTAMP" => Ok(Self::Timestamp),
_ => Err(()),
}
}
}
impl TemporalType {
pub fn parse(s: &str) -> Option<Self> {
s.parse().ok()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_temporal_type_from_str() {
assert_eq!(
TemporalType::parse("TRANSACTION"),
Some(TemporalType::Transaction)
);
assert_eq!(
TemporalType::parse("transaction"),
Some(TemporalType::Transaction)
);
assert_eq!(
TemporalType::parse("TIMESTAMP"),
Some(TemporalType::Timestamp)
);
assert_eq!(
TemporalType::parse("timestamp"),
Some(TemporalType::Timestamp)
);
assert_eq!(TemporalType::parse("INVALID"), None);
}
fn _assert_object_safe(_: &dyn Transaction) {}
}