mirror of
https://github.com/aljazceru/turso.git
synced 2026-02-11 03:04:22 +01:00
triggers: add in-memory schema entries
This commit is contained in:
161
core/schema.rs
161
core/schema.rs
@@ -74,6 +74,47 @@ impl Clone for View {
|
||||
/// Type alias for regular views collection
|
||||
pub type ViewsMap = HashMap<String, Arc<View>>;
|
||||
|
||||
/// Trigger structure
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Trigger {
|
||||
pub name: String,
|
||||
pub sql: String,
|
||||
pub table_name: String,
|
||||
pub time: turso_parser::ast::TriggerTime,
|
||||
pub event: turso_parser::ast::TriggerEvent,
|
||||
pub for_each_row: bool,
|
||||
pub when_clause: Option<turso_parser::ast::Expr>,
|
||||
pub commands: Vec<turso_parser::ast::TriggerCmd>,
|
||||
pub temporary: bool,
|
||||
}
|
||||
|
||||
impl Trigger {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn new(
|
||||
name: String,
|
||||
sql: String,
|
||||
table_name: String,
|
||||
time: Option<turso_parser::ast::TriggerTime>,
|
||||
event: turso_parser::ast::TriggerEvent,
|
||||
for_each_row: bool,
|
||||
when_clause: Option<turso_parser::ast::Expr>,
|
||||
commands: Vec<turso_parser::ast::TriggerCmd>,
|
||||
temporary: bool,
|
||||
) -> Self {
|
||||
Self {
|
||||
name,
|
||||
sql,
|
||||
table_name,
|
||||
time: time.unwrap_or(turso_parser::ast::TriggerTime::Before),
|
||||
event,
|
||||
for_each_row,
|
||||
when_clause,
|
||||
commands,
|
||||
temporary,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
use crate::storage::btree::{BTreeCursor, CursorTrait};
|
||||
use crate::translate::collate::CollationSeq;
|
||||
use crate::translate::plan::{SelectPlan, TableReferences};
|
||||
@@ -130,6 +171,9 @@ pub struct Schema {
|
||||
|
||||
pub views: ViewsMap,
|
||||
|
||||
/// table_name to list of triggers
|
||||
pub triggers: HashMap<String, Vec<Arc<Trigger>>>,
|
||||
|
||||
/// table_name to list of indexes for the table
|
||||
pub indexes: HashMap<String, VecDeque<Arc<Index>>>,
|
||||
pub has_indexes: std::collections::HashSet<String>,
|
||||
@@ -163,6 +207,7 @@ impl Schema {
|
||||
let materialized_view_sql = HashMap::new();
|
||||
let incremental_views = HashMap::new();
|
||||
let views: ViewsMap = HashMap::new();
|
||||
let triggers = HashMap::new();
|
||||
let table_to_materialized_views: HashMap<String, Vec<String>> = HashMap::new();
|
||||
let incompatible_views = HashSet::new();
|
||||
Self {
|
||||
@@ -171,6 +216,7 @@ impl Schema {
|
||||
materialized_view_sql,
|
||||
incremental_views,
|
||||
views,
|
||||
triggers,
|
||||
indexes,
|
||||
has_indexes,
|
||||
indexes_enabled,
|
||||
@@ -310,6 +356,71 @@ impl Schema {
|
||||
self.views.get(&name).cloned()
|
||||
}
|
||||
|
||||
pub fn add_trigger(&mut self, trigger: Trigger, table_name: &str) -> Result<()> {
|
||||
self.check_object_name_conflict(&trigger.name)?;
|
||||
let table_name = normalize_ident(table_name);
|
||||
|
||||
self.triggers
|
||||
.entry(table_name)
|
||||
.or_default()
|
||||
.push(Arc::new(trigger));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn remove_trigger(&mut self, name: &str) -> Result<()> {
|
||||
let name = normalize_ident(name);
|
||||
|
||||
let mut removed = false;
|
||||
for triggers_list in self.triggers.values_mut() {
|
||||
for i in 0..triggers_list.len() {
|
||||
let trigger = &triggers_list[i];
|
||||
if normalize_ident(&trigger.name) == name {
|
||||
removed = true;
|
||||
triggers_list.remove(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if removed {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if !removed {
|
||||
return Err(crate::LimboError::ParseError(format!(
|
||||
"no such trigger: {name}"
|
||||
)));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_trigger_for_table(&self, table_name: &str, name: &str) -> Option<Arc<Trigger>> {
|
||||
let table_name = normalize_ident(table_name);
|
||||
let name = normalize_ident(name);
|
||||
self.triggers
|
||||
.get(&table_name)
|
||||
.and_then(|triggers| triggers.iter().find(|t| t.name == name).cloned())
|
||||
}
|
||||
|
||||
pub fn get_triggers_for_table(
|
||||
&self,
|
||||
table_name: &str,
|
||||
) -> impl Iterator<Item = &Arc<Trigger>> + Clone {
|
||||
let table_name = normalize_ident(table_name);
|
||||
self.triggers
|
||||
.get(&table_name)
|
||||
.map(|triggers| triggers.iter())
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
pub fn get_trigger(&self, name: &str) -> Option<Arc<Trigger>> {
|
||||
let name = normalize_ident(name);
|
||||
self.triggers
|
||||
.values()
|
||||
.flatten()
|
||||
.find(|t| t.name == name)
|
||||
.cloned()
|
||||
}
|
||||
|
||||
pub fn add_btree_table(&mut self, table: Arc<BTreeTable>) -> Result<()> {
|
||||
self.check_object_name_conflict(&table.name)?;
|
||||
let name = normalize_ident(&table.name);
|
||||
@@ -856,6 +967,45 @@ impl Schema {
|
||||
}
|
||||
}
|
||||
}
|
||||
"trigger" => {
|
||||
use turso_parser::ast::{Cmd, Stmt};
|
||||
use turso_parser::parser::Parser;
|
||||
|
||||
let sql = maybe_sql.expect("sql should be present for trigger");
|
||||
let trigger_name = name.to_string();
|
||||
|
||||
let mut parser = Parser::new(sql.as_bytes());
|
||||
let Ok(Some(Cmd::Stmt(Stmt::CreateTrigger {
|
||||
temporary,
|
||||
if_not_exists: _,
|
||||
trigger_name: _,
|
||||
time,
|
||||
event,
|
||||
tbl_name,
|
||||
for_each_row,
|
||||
when_clause,
|
||||
commands,
|
||||
}))) = parser.next_cmd()
|
||||
else {
|
||||
return Err(crate::LimboError::ParseError(format!(
|
||||
"invalid trigger sql: {sql}"
|
||||
)));
|
||||
};
|
||||
self.add_trigger(
|
||||
Trigger::new(
|
||||
trigger_name.clone(),
|
||||
sql.to_string(),
|
||||
tbl_name.name.to_string(),
|
||||
time,
|
||||
event,
|
||||
for_each_row,
|
||||
when_clause.map(|e| *e),
|
||||
commands,
|
||||
temporary,
|
||||
),
|
||||
tbl_name.name.as_str(),
|
||||
)?;
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
|
||||
@@ -1198,6 +1348,16 @@ impl Clone for Schema {
|
||||
.iter()
|
||||
.map(|(name, view)| (name.clone(), Arc::new((**view).clone())))
|
||||
.collect();
|
||||
let triggers = self
|
||||
.triggers
|
||||
.iter()
|
||||
.map(|(table_name, triggers)| {
|
||||
(
|
||||
table_name.clone(),
|
||||
triggers.iter().map(|t| Arc::new((**t).clone())).collect(),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
let incompatible_views = self.incompatible_views.clone();
|
||||
Self {
|
||||
tables,
|
||||
@@ -1205,6 +1365,7 @@ impl Clone for Schema {
|
||||
materialized_view_sql,
|
||||
incremental_views,
|
||||
views,
|
||||
triggers,
|
||||
indexes,
|
||||
has_indexes: self.has_indexes.clone(),
|
||||
indexes_enabled: self.indexes_enabled,
|
||||
|
||||
Reference in New Issue
Block a user