1use 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}