radixdb_executor/mutation/
host.rs1use std::sync::{Arc, Mutex};
8
9use radixdb_catalog::{CatalogMutationSet, TriggerTiming};
10use radixdb_core::{DataType, IsolationLevel, Result, Row, RowVec};
11use radixdb_functions::FunctionRegistry;
12use radixdb_plugin_host::PluginRegistry;
13use radixdb_sql::ast::{
14 CreateJobStatement, CreateRoutineStatement, CreateTriggerStatement, Expression,
15 SelectStatement, Statement,
16};
17use radixdb_storage::mvcc::engine::MVCCEngine;
18use radixdb_storage::traits::{QueryResult, Table, Transaction};
19use rustc_hash::FxHashMap;
20
21use crate::catalog::DdlTransaction;
22use crate::context::ExecutionContext;
23use crate::procedural::{CallBoundary, DmlTriggerEvent, DmlTriggerPlan};
24
25#[doc(hidden)]
27pub struct ActiveTransaction {
28 pub transaction: Box<dyn Transaction>,
29 pub tables: FxHashMap<String, Box<dyn Table>>,
30 pub catalog: DdlTransaction,
31 catalog_savepoints: FxHashMap<String, DdlTransaction>,
32}
33
34impl ActiveTransaction {
35 pub fn new(transaction: Box<dyn Transaction>, catalog: DdlTransaction) -> Self {
36 Self {
37 transaction,
38 tables: FxHashMap::default(),
39 catalog,
40 catalog_savepoints: FxHashMap::default(),
41 }
42 }
43
44 pub fn create_savepoint(&mut self, name: &str) -> Result<()> {
45 self.transaction.create_savepoint(name)?;
46 self.catalog_savepoints
47 .insert(name.to_owned(), self.catalog.clone());
48 Ok(())
49 }
50
51 pub fn release_savepoint(&mut self, name: &str) -> Result<()> {
52 self.transaction.release_savepoint(name)?;
53 self.catalog_savepoints.remove(name);
54 Ok(())
55 }
56
57 pub fn rollback_to_savepoint(&mut self, name: &str) -> Result<()> {
58 let catalog = self.catalog_savepoints.get(name).cloned().ok_or_else(|| {
59 radixdb_core::Error::invalid_argument(format!(
60 "savepoint '{name}' has no catalog snapshot"
61 ))
62 })?;
63 let timestamp = self
64 .transaction
65 .get_savepoint_timestamp(name)
66 .ok_or_else(|| {
67 radixdb_core::Error::invalid_argument(format!(
68 "savepoint '{name}' has no storage timestamp"
69 ))
70 })?;
71 for table in self.tables.values() {
72 table.rollback_to_timestamp(timestamp);
73 }
74 self.transaction.rollback_to_savepoint(name)?;
75 self.catalog = catalog;
76 Ok(())
77 }
78
79 pub fn rollback(&mut self) -> Result<()> {
80 let result = self.transaction.rollback();
84 for (_, mut table) in self.tables.drain() {
85 table.rollback();
86 }
87 result
88 }
89
90 pub fn stage_catalog_for_commit(&mut self) -> Result<()> {
91 if let Some(mutation) = self.catalog.pending_mutation()? {
92 self.transaction.stage_catalog_mutation(mutation)?;
93 }
94 Ok(())
95 }
96
97 pub fn has_pending_catalog_changes(&self) -> bool {
98 self.catalog.has_pending_catalog_changes()
99 }
100
101 pub fn stage_catalog_statement(&mut self, statement: Statement) -> Result<()> {
102 self.catalog.stage_statement(statement)
103 }
104}
105
106#[doc(hidden)]
108#[derive(Debug, Clone, PartialEq, Eq)]
109pub struct BoundQueryColumn {
110 pub name: String,
111 pub data_type: DataType,
112 pub nullable: bool,
113}
114
115#[doc(hidden)]
117pub trait MutationHost {
118 fn mutation_engine(&self) -> &Arc<MVCCEngine>;
119
120 fn mutation_default_isolation_level(&self) -> IsolationLevel;
121
122 fn mutation_function_registry(&self) -> &Arc<FunctionRegistry>;
123
124 fn mutation_plugin_registry(&self) -> &Arc<PluginRegistry>;
125
126 fn mutation_active_transaction(&self) -> &Mutex<Option<ActiveTransaction>>;
127
128 fn mutation_execute_select(
129 &self,
130 statement: &SelectStatement,
131 ctx: &ExecutionContext,
132 ) -> Result<Box<dyn QueryResult>>;
133
134 fn mutation_describe_select_output(
135 &self,
136 statement: &SelectStatement,
137 ) -> Result<Vec<BoundQueryColumn>>;
138
139 fn mutation_compile_routine(
140 &self,
141 statement: &CreateRoutineStatement,
142 catalog: &radixdb_catalog::CatalogGeneration,
143 identity: radixdb_procedural::CompileIdentity,
144 search_path: Vec<radixdb_catalog::ObjectId>,
145 ) -> Result<Vec<radixdb_catalog::ObjectId>>;
146
147 fn mutation_validate_trigger(
148 &self,
149 statement: &CreateTriggerStatement,
150 catalog: &radixdb_catalog::CatalogGeneration,
151 ) -> Result<Vec<radixdb_catalog::ObjectId>>;
152
153 fn mutation_bind_job(
154 &self,
155 statement: &CreateJobStatement,
156 catalog: &radixdb_catalog::CatalogGeneration,
157 context: &ExecutionContext,
158 ) -> Result<crate::catalog::BoundJobDefinition>;
159
160 fn mutation_prepare_dml_triggers(
161 &self,
162 table_name: &str,
163 event: DmlTriggerEvent,
164 updated_columns: &[String],
165 context: &ExecutionContext,
166 ) -> Result<DmlTriggerPlan>;
167
168 fn mutation_begin_trigger_boundary(&self) -> Result<CallBoundary>;
169
170 fn mutation_complete_trigger_boundary(&self, boundary: &CallBoundary) -> Result<()>;
171
172 fn mutation_abort_trigger_boundary(&self, boundary: &CallBoundary) -> Result<()>;
173
174 fn mutation_fire_statement_triggers(
175 &self,
176 plan: &DmlTriggerPlan,
177 timing: TriggerTiming,
178 context: &ExecutionContext,
179 ) -> Result<()>;
180
181 fn mutation_fire_before_row_triggers(
182 &self,
183 plan: &DmlTriggerPlan,
184 old: Option<&Row>,
185 new: Option<Row>,
186 row_identity: Option<i64>,
187 context: &ExecutionContext,
188 ) -> Result<Option<Row>>;
189
190 fn mutation_fire_after_row_triggers(
191 &self,
192 plan: &DmlTriggerPlan,
193 old: Option<&Row>,
194 new: Option<&Row>,
195 row_identity: Option<i64>,
196 context: &ExecutionContext,
197 ) -> Result<()>;
198
199 fn mutation_process_where_subqueries(
200 &self,
201 expression: &Expression,
202 ctx: &ExecutionContext,
203 ) -> Result<Expression>;
204
205 fn mutation_process_correlated_expression(
206 &self,
207 expression: &Expression,
208 ctx: &ExecutionContext,
209 ) -> Result<Expression>;
210
211 fn mutation_process_correlated_where(
212 &self,
213 expression: &Expression,
214 ctx: &ExecutionContext,
215 ) -> Result<Expression>;
216
217 fn mutation_optimize_exists_to_semi_join(
218 &self,
219 expression: &Expression,
220 ctx: &ExecutionContext,
221 outer_tables: &[String],
222 outer_limit: Option<i64>,
223 ) -> Result<Option<Expression>>;
224
225 fn mutation_optimize_in_to_semi_join(
226 &self,
227 expression: &Expression,
228 ctx: &ExecutionContext,
229 outer_tables: &[String],
230 ) -> Result<Option<Expression>>;
231
232 fn mutation_has_subqueries(expression: &Expression) -> bool;
233
234 fn mutation_has_correlated_subqueries(expression: &Expression) -> bool;
235
236 fn mutation_materialize_result(result: Box<dyn QueryResult>) -> Result<RowVec>;
237
238 fn mutation_invalidate_query_cache(&self, table_name: &str);
239
240 fn mutation_invalidate_semantic_cache(&self, table_name: &str);
241
242 fn mutation_invalidate_authorization_caches(&self);
243
244 fn mutation_active_transaction_id(&self) -> Option<i64> {
245 self.mutation_active_transaction()
246 .lock()
247 .unwrap()
248 .as_ref()
249 .map(|state| state.transaction.id())
250 }
251
252 fn mutation_has_active_transaction(&self) -> bool {
253 self.mutation_active_transaction().lock().unwrap().is_some()
254 }
255
256 fn mutation_stage_catalog_statement(
261 &self,
262 statement: Statement,
263 ) -> Result<Option<CatalogMutationSet>> {
264 let mut active = self.mutation_active_transaction().lock().unwrap();
265 if let Some(state) = active.as_mut() {
266 state.stage_catalog_statement(statement)?;
267 return Ok(None);
268 }
269 drop(active);
270
271 let generation = self.mutation_engine().pin_catalog()?;
272 let mut catalog = DdlTransaction::begin_shared_with_plugin_registry(
273 generation,
274 Arc::clone(self.mutation_plugin_registry()),
275 );
276 catalog.stage_statement(statement)?;
277 catalog.pending_mutation()
278 }
279
280 fn mutation_stage_catalog_statement_as(
281 &self,
282 statement: Statement,
283 actor: radixdb_catalog::ObjectId,
284 current_database: Option<&str>,
285 ) -> Result<Option<CatalogMutationSet>> {
286 let mut active = self.mutation_active_transaction().lock().unwrap();
287 if let Some(state) = active.as_mut() {
288 state
289 .catalog
290 .stage_statement_as(statement, actor, current_database)?;
291 return Ok(None);
292 }
293 drop(active);
294
295 let generation = self.mutation_engine().pin_catalog()?;
296 let mut catalog = DdlTransaction::begin_shared_with_plugin_registry(
297 generation,
298 Arc::clone(self.mutation_plugin_registry()),
299 );
300 catalog.stage_statement_as(statement, actor, current_database)?;
301 catalog.pending_mutation()
302 }
303}