Skip to main content

radixdb_executor/
executor_host.rs

1//! Internal composition callbacks for executor-owned statement routing.
2
3use crate::aggregation::AggregationHost;
4use crate::binding::output::{NavigationOutputBinding, OutputBindingHost};
5use crate::binding::source::SourceBindingHost;
6use crate::cte::CteHost;
7use crate::dispatch::program::{CachedExecutionHost, CachedFastPathHost};
8use crate::dispatch::statement::StatementDispatchHost;
9use crate::mutation::dml_fast_path::DmlFastPathExt;
10use crate::mutation::pk_fast_path::PkFastPathExt;
11use crate::navigation::{NavigationExecutorExt, NavigationHost};
12use crate::subquery::{SubqueryExecutorExt, SubqueryHost};
13use crate::window::WindowHost;
14use radixdb_core::{Result, Value};
15use radixdb_functions::FunctionRegistry;
16use radixdb_sql::ast::*;
17use radixdb_storage::mvcc::engine::MVCCEngine;
18use radixdb_storage::traits::{Engine, QueryResult};
19
20use super::navigation::{self, ReferenceExpandPlan};
21use super::query_cache::{CachedPlanRef, QueryCache};
22use super::Executor;
23use crate::context::ExecutionContext;
24
25impl SubqueryHost for Executor {
26    fn subquery_engine(&self) -> &std::sync::Arc<MVCCEngine> {
27        &self.engine
28    }
29
30    fn subquery_open_table(
31        &self,
32        table_name: &str,
33    ) -> Result<crate::access::handle::QueryTableHandle> {
34        crate::access::handle::open_query_table_raw(
35            &self.engine,
36            &self.active_transaction,
37            table_name,
38        )
39    }
40
41    fn subquery_execute_select(
42        &self,
43        statement: &SelectStatement,
44        context: &ExecutionContext,
45    ) -> Result<Box<dyn radixdb_storage::traits::QueryResult>> {
46        self.execute_select(statement, context)
47    }
48}
49
50impl CteHost for Executor {
51    fn cte_function_registry(&self) -> &FunctionRegistry {
52        self.function_registry.as_ref()
53    }
54
55    fn cte_execute_select(
56        &self,
57        statement: &SelectStatement,
58        context: &ExecutionContext,
59    ) -> Result<Box<dyn QueryResult>> {
60        self.execute_select(statement, context)
61    }
62}
63
64impl NavigationHost for Executor {
65    fn navigation_engine(&self) -> &std::sync::Arc<MVCCEngine> {
66        &self.engine
67    }
68
69    fn navigation_active_transaction(
70        &self,
71    ) -> &std::sync::Mutex<Option<crate::mutation::host::ActiveTransaction>> {
72        &self.active_transaction
73    }
74
75    fn navigation_execute_select(
76        &self,
77        statement: &SelectStatement,
78        context: &ExecutionContext,
79    ) -> Result<Box<dyn QueryResult>> {
80        self.execute_select(statement, context)
81    }
82
83    fn navigation_project_rows_with_alias(
84        &self,
85        select_expressions: &[Expression],
86        rows: radixdb_core::RowVec,
87        columns: &[String],
88        columns_lower: Option<&[String]>,
89        context: &ExecutionContext,
90        table_alias: Option<&str>,
91    ) -> Result<radixdb_core::RowVec> {
92        self.project_rows_with_alias(
93            select_expressions,
94            rows,
95            columns,
96            columns_lower,
97            context,
98            table_alias,
99        )
100    }
101
102    fn navigation_source_materialized(
103        &self,
104        plan: &crate::navigation::ReferenceExpandPlan,
105        context: &ExecutionContext,
106    ) {
107        #[cfg(any(test, feature = "test-hooks"))]
108        navigation::run_source_materialized_test_hook(plan, context);
109        #[cfg(not(any(test, feature = "test-hooks")))]
110        let _ = (plan, context);
111    }
112}
113
114impl WindowHost for Executor {
115    fn window_function_registry(&self) -> &FunctionRegistry {
116        self.function_registry.as_ref()
117    }
118}
119
120impl AggregationHost for Executor {
121    fn aggregation_engine(&self) -> &std::sync::Arc<MVCCEngine> {
122        &self.engine
123    }
124
125    fn aggregation_function_registry(&self) -> &FunctionRegistry {
126        self.function_registry.as_ref()
127    }
128
129    fn aggregation_active_transaction(
130        &self,
131    ) -> &std::sync::Mutex<Option<crate::mutation::host::ActiveTransaction>> {
132        &self.active_transaction
133    }
134
135    fn aggregation_process_where_subqueries(
136        &self,
137        expression: &Expression,
138        context: &ExecutionContext,
139    ) -> Result<Expression> {
140        self.process_where_subqueries(expression, context)
141    }
142
143    fn aggregation_try_process_select_subqueries(
144        &self,
145        columns: &[Expression],
146        context: &ExecutionContext,
147    ) -> Result<Option<Vec<Expression>>> {
148        self.try_process_select_subqueries(columns, context)
149    }
150
151    fn aggregation_has_correlated_subqueries(&self, expression: &Expression) -> bool {
152        Self::has_correlated_subqueries(expression)
153    }
154
155    fn aggregation_process_correlated_expression(
156        &self,
157        expression: &Expression,
158        context: &ExecutionContext,
159    ) -> Result<Expression> {
160        self.process_correlated_expression(expression, context)
161    }
162
163    fn aggregation_output_column_names(
164        &self,
165        select_expressions: &[Expression],
166        source_columns: &[String],
167        table_alias: Option<&str>,
168    ) -> Vec<String> {
169        self.get_output_column_names(select_expressions, source_columns, table_alias)
170    }
171}
172
173impl OutputBindingHost for Executor {
174    fn output_binding_engine(&self) -> &MVCCEngine {
175        self.engine.as_ref()
176    }
177
178    fn output_binding_functions(&self) -> &FunctionRegistry {
179        self.function_registry.as_ref()
180    }
181
182    fn output_binding_type_name(
183        &self,
184        name: &str,
185    ) -> Result<(radixdb_core::DataType, radixdb_core::LogicalTypeRef, String)> {
186        let (catalog, _) = crate::procedural::transaction_visible_catalog(self)?;
187        let data_type = crate::catalog::bind_catalog_type_in_generation(name, catalog.as_ref())?;
188        Ok((
189            data_type.logical_type(),
190            data_type.logical_type_ref(),
191            crate::procedural::catalog_type_name(catalog.as_ref(), data_type),
192        ))
193    }
194
195    fn output_binding_stored_function(
196        &self,
197        name: &str,
198        argument_types: &[Option<radixdb_core::LogicalTypeRef>],
199    ) -> Result<
200        Option<(
201            radixdb_core::DataType,
202            radixdb_core::LogicalTypeRef,
203            String,
204            bool,
205        )>,
206    > {
207        crate::procedural::function::bind_stored_function_result(self, name, argument_types).map(
208            |bound| {
209                bound.map(|bound| {
210                    (
211                        bound.result_type,
212                        bound.logical_type,
213                        bound.type_name,
214                        bound.nullable,
215                    )
216                })
217            },
218        )
219    }
220
221    fn output_binding_table_schema(
222        &self,
223        name_lower: &str,
224    ) -> Result<radixdb_core::CompactArc<radixdb_core::Schema>> {
225        let transaction_id = self
226            .active_transaction
227            .lock()
228            .unwrap()
229            .as_ref()
230            .map(|state| state.transaction.id());
231        transaction_id.map_or_else(
232            || self.engine.get_table_schema(name_lower),
233            |transaction_id| {
234                self.engine
235                    .get_table_schema_for_txn(transaction_id, name_lower)
236            },
237        )
238    }
239
240    fn output_binding_view(
241        &self,
242        name_lower: &str,
243    ) -> Result<Option<std::sync::Arc<radixdb_storage::mvcc::ViewDefinition>>> {
244        self.visible_view_lowercase(name_lower)
245    }
246
247    fn output_binding_navigation(
248        &self,
249        select: &SelectStatement,
250    ) -> Result<Vec<NavigationOutputBinding>> {
251        navigation::bind_navigation_paths(self.engine.as_ref(), select).map(|paths| {
252            paths
253                .into_iter()
254                .map(|path| NavigationOutputBinding {
255                    display_path: path.display_path().to_string(),
256                    terminal_type: path.terminal_type(),
257                    nullable: path.nullable(),
258                })
259                .collect()
260        })
261    }
262}
263
264impl SourceBindingHost for Executor {
265    type BindingCache = navigation::CachedReferenceExpand;
266
267    fn source_binding_engine(&self) -> &MVCCEngine {
268        self.engine.as_ref()
269    }
270
271    fn source_binding_functions(&self) -> &FunctionRegistry {
272        self.function_registry.as_ref()
273    }
274
275    fn source_binding_query_cache(&self) -> &QueryCache {
276        &self.query_cache
277    }
278
279    fn source_binding_view(
280        &self,
281        name_lower: &str,
282    ) -> Result<Option<std::sync::Arc<radixdb_storage::mvcc::ViewDefinition>>> {
283        self.visible_view_lowercase(name_lower)
284    }
285}
286
287impl CachedExecutionHost<navigation::CachedReferenceExpand> for Executor {
288    fn dispatch_query_cache(&self) -> &QueryCache {
289        &self.query_cache
290    }
291
292    fn dispatch_execute_bound_plan(
293        &self,
294        plan: &CachedPlanRef,
295        context: &ExecutionContext,
296    ) -> Result<Box<dyn QueryResult>> {
297        self.execute_bound_cached_plan(plan, context)
298    }
299
300    fn dispatch_execute_statement(
301        &self,
302        statement: &Statement,
303        context: &ExecutionContext,
304    ) -> Result<Box<dyn QueryResult>> {
305        self.execute_statement(statement, context)
306    }
307}
308
309impl CachedFastPathHost<navigation::CachedReferenceExpand> for Executor {
310    fn dispatch_fast_path_blocked(&self) -> bool {
311        self.active_transaction
312            .try_lock()
313            .map_or(true, |active| active.is_some())
314    }
315
316    fn dispatch_fast_path_binding_is_nonempty(&self, plan: &CachedPlanRef) -> Result<bool> {
317        Ok(self
318            .bind_cached_reference_expand(plan.statement(), plan.binding_cache())?
319            .is_some()
320            && matches!(plan.statement(), Statement::Select(_)))
321    }
322
323    fn dispatch_try_borrowed_param_fast_path(
324        &self,
325        plan: &CachedPlanRef,
326        params: &[Value],
327    ) -> Option<Result<Box<dyn QueryResult>>> {
328        match plan.statement() {
329            Statement::Select(statement) => {
330                self.try_fast_pk_lookup_with_params(statement, params, plan.compiled_state())
331            }
332            Statement::Update(statement) => {
333                self.try_fast_pk_update_with_params(statement, params, plan.compiled_state())
334            }
335            Statement::Delete(statement) => {
336                self.try_fast_pk_delete_with_params(statement, params, plan.compiled_state())
337            }
338            _ => None,
339        }
340    }
341}
342
343impl StatementDispatchHost for Executor {
344    type NavigationPlan = ReferenceExpandPlan;
345
346    fn dispatch_ddl_fence_already_held(&self) -> bool {
347        self.ddl_fence_already_held
348    }
349
350    fn dispatch_authorize_statement(
351        &self,
352        statement: &Statement,
353        context: &ExecutionContext,
354    ) -> Result<()> {
355        crate::authorization::authorize_statement(self, statement, context)
356    }
357
358    fn dispatch_bind_navigation(
359        &self,
360        statement: &Statement,
361    ) -> Result<Option<Self::NavigationPlan>> {
362        match statement {
363            Statement::Select(select) => {
364                navigation::bind_reference_expand_plan(self.engine.as_ref(), select)
365            }
366            Statement::Explain(explain) => {
367                if let Statement::Select(select) = explain.statement.as_ref() {
368                    navigation::bind_reference_expand_plan(self.engine.as_ref(), select)?;
369                } else {
370                    navigation::reject_navigation_in_write_statement(
371                        self.engine.as_ref(),
372                        statement,
373                    )?;
374                }
375                Ok(None)
376            }
377            _ => {
378                navigation::reject_navigation_in_write_statement(self.engine.as_ref(), statement)?;
379                Ok(None)
380            }
381        }
382    }
383
384    fn dispatch_select(
385        &self,
386        statement: &SelectStatement,
387        navigation: Option<&Self::NavigationPlan>,
388        context: &ExecutionContext,
389    ) -> Result<Box<dyn QueryResult>> {
390        if let Some(plan) = navigation {
391            return self.execute_reference_projection(statement, plan, context);
392        }
393        if let Some(result) = self.try_fast_pk_lookup(statement, context) {
394            return result;
395        }
396        self.execute_select(statement, context)
397    }
398
399    fn dispatch_call(
400        &self,
401        statement: &CallStatement,
402        context: &ExecutionContext,
403    ) -> Result<Box<dyn QueryResult>> {
404        self.execute_call_statement(statement, context)
405    }
406
407    fn dispatch_set(
408        &self,
409        statement: &SetStatement,
410        context: &ExecutionContext,
411    ) -> Result<Box<dyn QueryResult>> {
412        self.execute_set(statement, context)
413    }
414
415    fn dispatch_show_tables(
416        &self,
417        statement: &ShowTablesStatement,
418        context: &ExecutionContext,
419    ) -> Result<Box<dyn QueryResult>> {
420        self.execute_show_tables(statement, context)
421    }
422
423    fn dispatch_show_views(
424        &self,
425        statement: &ShowViewsStatement,
426        context: &ExecutionContext,
427    ) -> Result<Box<dyn QueryResult>> {
428        self.execute_show_views(statement, context)
429    }
430
431    fn dispatch_show_create_table(
432        &self,
433        statement: &ShowCreateTableStatement,
434        context: &ExecutionContext,
435    ) -> Result<Box<dyn QueryResult>> {
436        self.execute_show_create_table(statement, context)
437    }
438
439    fn dispatch_show_create_view(
440        &self,
441        statement: &ShowCreateViewStatement,
442        context: &ExecutionContext,
443    ) -> Result<Box<dyn QueryResult>> {
444        self.execute_show_create_view(statement, context)
445    }
446
447    fn dispatch_show_indexes(
448        &self,
449        statement: &ShowIndexesStatement,
450        context: &ExecutionContext,
451    ) -> Result<Box<dyn QueryResult>> {
452        self.execute_show_indexes(statement, context)
453    }
454
455    fn dispatch_describe(
456        &self,
457        statement: &DescribeStatement,
458        context: &ExecutionContext,
459    ) -> Result<Box<dyn QueryResult>> {
460        self.execute_describe(statement, context)
461    }
462
463    fn dispatch_pragma(
464        &self,
465        statement: &PragmaStatement,
466        context: &ExecutionContext,
467    ) -> Result<Box<dyn QueryResult>> {
468        self.execute_pragma(statement, context)
469    }
470
471    fn dispatch_expression(
472        &self,
473        statement: &ExpressionStatement,
474        context: &ExecutionContext,
475    ) -> Result<Box<dyn QueryResult>> {
476        self.execute_expression_stmt(statement, context)
477    }
478
479    fn dispatch_explain(
480        &self,
481        statement: &ExplainStatement,
482        context: &ExecutionContext,
483    ) -> Result<Box<dyn QueryResult>> {
484        self.execute_explain(statement, context)
485    }
486
487    fn dispatch_analyze(
488        &self,
489        statement: &AnalyzeStatement,
490        context: &ExecutionContext,
491    ) -> Result<Box<dyn QueryResult>> {
492        self.execute_analyze(statement, context)
493    }
494
495    fn dispatch_vacuum(
496        &self,
497        statement: &VacuumStatement,
498        context: &ExecutionContext,
499    ) -> Result<Box<dyn QueryResult>> {
500        self.execute_vacuum(statement, context)
501    }
502}