Skip to main content

reifydb_engine/vm/
executor.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2026 ReifyDB
3
4use std::{ops::Deref, result::Result as StdResult, sync::Arc};
5
6use bumpalo::Bump;
7use reifydb_catalog::{catalog::Catalog, vtable::system::flow_operator_store::SystemFlowOperatorStore};
8use reifydb_core::{
9	error::diagnostic::subscription,
10	execution::ExecutionResult,
11	interface::catalog::policy::SessionOp,
12	metric::{ExecutionMetrics, StatementMetric},
13	value::column::columns::Columns,
14};
15use reifydb_metric::storage::metric::MetricReader;
16use reifydb_policy::inject_from_policies;
17use reifydb_rql::{
18	ast::parse_str,
19	compiler::{CompilationResult, Compiled, IncrementalCompilation, constrain_policy},
20	fingerprint::request::fingerprint_request,
21};
22use reifydb_runtime::context::clock::Instant;
23use reifydb_store_single::SingleStore;
24use reifydb_transaction::transaction::{
25	RqlExecutor, TestTransaction, Transaction, admin::AdminTransaction, command::CommandTransaction,
26	query::QueryTransaction,
27};
28#[cfg(not(reifydb_single_threaded))]
29use reifydb_value::error::Diagnostic;
30use reifydb_value::{
31	error::Error,
32	params::Params,
33	value::{Value, duration::Duration, frame::frame::Frame, value_type::ValueType},
34};
35use tracing::instrument;
36
37#[cfg(not(reifydb_single_threaded))]
38use crate::remote;
39use crate::{
40	Result,
41	policy::PolicyEvaluator,
42	vm::{
43		Admin, Command, Query, Subscription, Test,
44		services::{EngineConfig, Services},
45		stack::{SymbolTable, Variable},
46		vm::Vm,
47	},
48};
49
50pub struct Executor(Arc<Services>);
51
52impl Clone for Executor {
53	fn clone(&self) -> Self {
54		Self(self.0.clone())
55	}
56}
57
58impl Deref for Executor {
59	type Target = Services;
60
61	fn deref(&self) -> &Self::Target {
62		&self.0
63	}
64}
65
66impl Executor {
67	pub fn new(
68		catalog: Catalog,
69		config: EngineConfig,
70		flow_operator_store: SystemFlowOperatorStore,
71		stats_reader: MetricReader<SingleStore>,
72	) -> Self {
73		Self(Arc::new(Services::new(catalog, config, flow_operator_store, stats_reader)))
74	}
75
76	pub fn services(&self) -> &Arc<Services> {
77		&self.0
78	}
79
80	pub fn from_services(services: Arc<Services>) -> Self {
81		Self(services)
82	}
83
84	#[allow(dead_code)]
85	pub fn testing() -> Self {
86		Self(Services::testing())
87	}
88
89	#[cfg(not(reifydb_single_threaded))]
90	fn try_forward_remote_query(&self, err: &Error, rql: &str, params: Params) -> Result<Option<Vec<Frame>>> {
91		if let Some(ref registry) = self.0.remote_registry
92			&& remote::is_remote_query(err)
93			&& let Some(address) = remote::extract_remote_address(err)
94		{
95			let token = remote::extract_remote_token(err);
96			return registry.forward_query(&address, rql, params, token.as_deref()).map(Some);
97		}
98		Ok(None)
99	}
100}
101
102impl RqlExecutor for Executor {
103	fn rql(&self, tx: &mut Transaction<'_>, rql: &str, params: Params) -> ExecutionResult {
104		Executor::rql(self, tx, rql, params)
105	}
106}
107
108fn populate_symbols(symbols: &mut SymbolTable, params: &Params) -> Result<()> {
109	match params {
110		Params::Positional(values) => {
111			for (index, value) in values.iter().enumerate() {
112				let param_name = (index + 1).to_string();
113				symbols.set(param_name, Variable::scalar(value.clone()), false)?;
114			}
115		}
116		Params::Named(map) => {
117			for (name, value) in map.iter() {
118				symbols.set(name.clone(), Variable::scalar(value.clone()), false)?;
119			}
120		}
121		Params::None => {}
122	}
123	Ok(())
124}
125
126fn populate_identity(symbols: &mut SymbolTable, catalog: &Catalog, tx: &mut Transaction<'_>) -> Result<()> {
127	let identity = tx.identity();
128	if identity.is_privileged() {
129		return Ok(());
130	}
131	let attributes = catalog.list_identity_attributes(tx)?;
132	if identity.is_anonymous() {
133		let mut fields = vec![
134			("id".to_string(), Value::IdentityId(identity)),
135			("name".to_string(), Value::none_of(ValueType::Utf8)),
136			("roles".to_string(), Value::List(vec![])),
137		];
138		for attribute in &attributes {
139			fields.push((attribute.name.clone(), Value::none_of(attribute.value_type.clone())));
140		}
141		let columns = Columns::single_row(fields.iter().map(|(name, value)| (name.as_str(), value.clone())));
142		symbols.set("identity".to_string(), Variable::columns(columns), false)?;
143		return Ok(());
144	}
145	if let Some(user) = catalog.find_identity(tx, identity)? {
146		let roles = catalog.find_role_names_for_identity(tx, identity)?;
147		let role_values: Vec<Value> = roles.into_iter().map(Value::Utf8).collect();
148		let values = catalog.find_identity_attribute_values(tx, identity)?;
149		let mut fields = vec![
150			("id".to_string(), Value::IdentityId(identity)),
151			("name".to_string(), Value::Utf8(user.name)),
152			("roles".to_string(), Value::List(role_values)),
153		];
154		for attribute in &attributes {
155			let value = values
156				.iter()
157				.find(|v| v.attribute == attribute.id)
158				.map(|v| v.value.clone())
159				.unwrap_or_else(|| Value::none_of(attribute.value_type.clone()));
160			fields.push((attribute.name.clone(), value));
161		}
162		let columns = Columns::single_row(fields.iter().map(|(name, value)| (name.as_str(), value.clone())));
163		symbols.set("identity".to_string(), Variable::columns(columns), false)?;
164	}
165	Ok(())
166}
167
168type CompiledUnitsResult = (Vec<Frame>, Vec<Frame>, SymbolTable, Vec<StatementMetric>);
169
170struct ExecutionFailure {
171	error: Error,
172	partial_metrics: Vec<StatementMetric>,
173}
174
175fn build_metrics(statements: Vec<StatementMetric>) -> ExecutionMetrics {
176	let fps: Vec<_> = statements.iter().map(|m| m.fingerprint).collect();
177	ExecutionMetrics {
178		fingerprint: fingerprint_request(&fps),
179		statements,
180		..Default::default()
181	}
182}
183
184struct RunUnitOutcome {
185	symbols: SymbolTable,
186	run_result: Result<()>,
187	execute_duration_us: u64,
188}
189
190#[instrument(
191	name = "vm::run",
192	level = "debug",
193	skip_all,
194	fields(fingerprint = ?compiled.fingerprint, instr_count = compiled.instructions.len()),
195)]
196fn run_compiled_unit(
197	services: &Arc<Services>,
198	tx: &mut Transaction<'_>,
199	compiled: &Compiled,
200	params: &Params,
201	symbols: SymbolTable,
202	result: &mut Vec<Frame>,
203) -> RunUnitOutcome {
204	let mut vm = Vm::from_services(symbols, services, params, tx.identity());
205	let start = services.runtime_context.clock.instant();
206	let run_result = vm.run(services, tx, &compiled.instructions, result);
207	let execute_duration = start.elapsed();
208	RunUnitOutcome {
209		symbols: vm.symbols,
210		run_result,
211		execute_duration_us: execute_duration.as_micros() as u64,
212	}
213}
214
215#[instrument(
216	name = "executor::execute_units",
217	level = "debug",
218	skip_all,
219	fields(unit_count = compiled_list.len()),
220)]
221fn execute_compiled_units(
222	services: &Arc<Services>,
223	tx: &mut Transaction<'_>,
224	compiled_list: &[Compiled],
225	params: &Params,
226	mut symbols: SymbolTable,
227	compile_duration: Duration,
228) -> StdResult<CompiledUnitsResult, ExecutionFailure> {
229	let compile_duration_us = compile_duration.to_std().as_micros() as u64 / compiled_list.len().max(1) as u64;
230	let mut result = vec![];
231	let mut output_results: Vec<Frame> = Vec::new();
232	let mut metrics = Vec::new();
233
234	for compiled in compiled_list.iter() {
235		result.clear();
236		let outcome = run_compiled_unit(services, tx, compiled, params, symbols, &mut result);
237		symbols = outcome.symbols;
238
239		metrics.push(StatementMetric {
240			fingerprint: compiled.fingerprint,
241			normalized_rql: compiled.normalized_rql.clone(),
242			compile_duration_us,
243			execute_duration_us: outcome.execute_duration_us,
244			rows_affected: if outcome.run_result.is_ok() {
245				extract_rows_affected(&result)
246			} else {
247				0
248			},
249		});
250
251		if let Err(error) = outcome.run_result {
252			return Err(ExecutionFailure {
253				error,
254				partial_metrics: metrics,
255			});
256		}
257
258		if compiled.is_output {
259			output_results.append(&mut result);
260		}
261	}
262
263	Ok((output_results, result, symbols, metrics))
264}
265
266fn merge_results(mut output_results: Vec<Frame>, mut remaining: Vec<Frame>) -> Vec<Frame> {
267	output_results.append(&mut remaining);
268	output_results
269}
270
271#[inline]
272fn error_result(error: Error, metrics: ExecutionMetrics) -> ExecutionResult {
273	ExecutionResult {
274		frames: vec![],
275		error: Some(error),
276		metrics,
277	}
278}
279
280fn extract_rows_affected(result: &[Frame]) -> u64 {
281	if result.len() == 1 {
282		let frame = &result[0];
283		for col in &frame.columns {
284			match col.name.as_str() {
285				"inserted" | "updated" | "deleted" => {
286					if col.data.len() == 1
287						&& let Value::Uint8(n) = col.data.get_value(0)
288					{
289						return n;
290					}
291				}
292				_ => {}
293			}
294		}
295	}
296	result.len() as u64
297}
298
299impl Executor {
300	#[instrument(name = "executor::setup_symbols", level = "debug", skip_all)]
301	fn setup_symbols(&self, params: &Params, tx: &mut Transaction<'_>) -> Result<SymbolTable> {
302		let mut symbols = SymbolTable::new();
303		populate_symbols(&mut symbols, params)?;
304		populate_identity(&mut symbols, &self.catalog, tx)?;
305		Ok(symbols)
306	}
307
308	#[instrument(name = "executor::compile", level = "debug", skip(self, tx), fields(rql = %rql))]
309	fn compile_query(&self, tx: &mut Transaction<'_>, rql: &str) -> Result<CompilationResult> {
310		self.compiler.compile_with_policy(tx, rql, inject_from_policies)
311	}
312
313	#[instrument(name = "executor::rql", level = "debug", skip(self, tx, params), fields(rql = %rql))]
314	pub fn rql(&self, tx: &mut Transaction<'_>, rql: &str, params: Params) -> ExecutionResult {
315		let symbols = match self.setup_symbols(&params, tx) {
316			Ok(s) => s,
317			Err(e) => return error_result(e, ExecutionMetrics::default()),
318		};
319
320		let start_compile = self.0.runtime_context.clock.instant();
321		let compiled_list = match self.compile_query(tx, rql) {
322			Ok(CompilationResult::Ready(compiled)) => compiled,
323			Ok(CompilationResult::Incremental(_)) => {
324				unreachable!("incremental compilation not supported in rql()")
325			}
326			Err(err) => return self.handle_rql_compile_error(err, rql, params),
327		};
328		let compile_duration = Duration::from_std(start_compile.elapsed());
329
330		match self.run_units_collecting_last(tx, &compiled_list, &params, symbols, compile_duration) {
331			Ok((frames, metrics)) => ExecutionResult {
332				frames,
333				error: None,
334				metrics: build_metrics(metrics),
335			},
336			Err(f) => error_result(f.error, build_metrics(f.partial_metrics)),
337		}
338	}
339
340	#[inline]
341	#[cfg_attr(reifydb_single_threaded, allow(unused_variables))]
342	fn handle_rql_compile_error(&self, err: Error, rql: &str, params: Params) -> ExecutionResult {
343		#[cfg(not(reifydb_single_threaded))]
344		if let Ok(Some(frames)) = self.try_forward_remote_query(&err, rql, params) {
345			return ExecutionResult {
346				frames,
347				error: None,
348				metrics: ExecutionMetrics::default(),
349			};
350		}
351		error_result(err, ExecutionMetrics::default())
352	}
353
354	#[inline]
355	fn run_units_collecting_last(
356		&self,
357		tx: &mut Transaction<'_>,
358		compiled_list: &[Compiled],
359		params: &Params,
360		mut symbols: SymbolTable,
361		compile_duration: Duration,
362	) -> StdResult<(Vec<Frame>, Vec<StatementMetric>), ExecutionFailure> {
363		let compile_duration_us =
364			compile_duration.to_std().as_micros() as u64 / compiled_list.len().max(1) as u64;
365		let mut result = vec![];
366		let mut metrics = Vec::new();
367		for compiled in compiled_list.iter() {
368			result.clear();
369			let outcome = run_compiled_unit(&self.0, tx, compiled, params, symbols, &mut result);
370			symbols = outcome.symbols;
371
372			metrics.push(StatementMetric {
373				fingerprint: compiled.fingerprint,
374				normalized_rql: compiled.normalized_rql.clone(),
375				compile_duration_us,
376				execute_duration_us: outcome.execute_duration_us,
377				rows_affected: if outcome.run_result.is_ok() {
378					extract_rows_affected(&result)
379				} else {
380					0
381				},
382			});
383
384			if let Err(error) = outcome.run_result {
385				return Err(ExecutionFailure {
386					error,
387					partial_metrics: metrics,
388				});
389			}
390		}
391
392		Ok((result, metrics))
393	}
394
395	#[instrument(name = "executor::admin", level = "debug", skip(self, txn, cmd), fields(rql = %cmd.rql))]
396	pub fn admin(&self, txn: &mut AdminTransaction, cmd: Admin<'_>) -> ExecutionResult {
397		let symbols = match self.setup_symbols(&cmd.params, &mut Transaction::Admin(&mut *txn)) {
398			Ok(s) => s,
399			Err(e) => return error_result(e, ExecutionMetrics::default()),
400		};
401		if let Err(e) = self.enforce_admin_policy(&symbols, txn) {
402			return error_result(e, ExecutionMetrics::default());
403		}
404		let start_compile = self.0.runtime_context.clock.instant();
405		match self.compile_query(&mut Transaction::Admin(txn), cmd.rql) {
406			Err(err) => self.handle_admin_compile_error(err, cmd.rql, cmd.params),
407			Ok(CompilationResult::Ready(compiled)) => {
408				self.execute_admin_ready(txn, compiled, &cmd.params, symbols, start_compile)
409			}
410			Ok(CompilationResult::Incremental(state)) => {
411				self.execute_admin_incremental(txn, state, &cmd.params, symbols)
412			}
413		}
414	}
415
416	#[inline]
417	fn enforce_admin_policy(&self, symbols: &SymbolTable, txn: &mut AdminTransaction) -> Result<()> {
418		PolicyEvaluator::new(&self.0, symbols).enforce_session_policy(
419			&mut Transaction::Admin(txn),
420			SessionOp::Admin,
421			true,
422		)
423	}
424
425	#[inline]
426	#[cfg_attr(reifydb_single_threaded, allow(unused_variables))]
427	fn handle_admin_compile_error(&self, err: Error, rql: &str, params: Params) -> ExecutionResult {
428		#[cfg(not(reifydb_single_threaded))]
429		if let Ok(Some(frames)) = self.try_forward_remote_query(&err, rql, params) {
430			return ExecutionResult {
431				frames,
432				error: None,
433				metrics: ExecutionMetrics::default(),
434			};
435		}
436		error_result(err, ExecutionMetrics::default())
437	}
438
439	#[inline]
440	fn execute_admin_ready(
441		&self,
442		txn: &mut AdminTransaction,
443		compiled: Arc<Vec<Compiled>>,
444		params: &Params,
445		symbols: SymbolTable,
446		start_compile: Instant,
447	) -> ExecutionResult {
448		let compile_duration = Duration::from_std(start_compile.elapsed());
449		match execute_compiled_units(
450			&self.0,
451			&mut Transaction::Admin(txn),
452			&compiled,
453			params,
454			symbols,
455			compile_duration,
456		) {
457			Ok((output, remaining, _, metrics)) => ExecutionResult {
458				frames: merge_results(output, remaining),
459				error: None,
460				metrics: build_metrics(metrics),
461			},
462			Err(f) => ExecutionResult {
463				frames: vec![],
464				error: Some(f.error),
465				metrics: build_metrics(f.partial_metrics),
466			},
467		}
468	}
469
470	fn execute_admin_incremental(
471		&self,
472		txn: &mut AdminTransaction,
473		mut state: IncrementalCompilation,
474		params: &Params,
475		symbols: SymbolTable,
476	) -> ExecutionResult {
477		let policy = constrain_policy(inject_from_policies);
478		let mut result = vec![];
479		let mut output_results: Vec<Frame> = Vec::new();
480		let mut symbols = symbols;
481		let mut metrics = Vec::new();
482		loop {
483			let start_incr = self.0.runtime_context.clock.instant();
484			let next = match self.compiler.compile_next_with_policy(
485				&mut Transaction::Admin(txn),
486				&mut state,
487				&policy,
488			) {
489				Ok(n) => n,
490				Err(e) => return error_result(e, build_metrics(metrics)),
491			};
492			let compile_duration = start_incr.elapsed();
493
494			let Some(compiled) = next else {
495				break;
496			};
497
498			result.clear();
499			let mut tx = Transaction::Admin(txn);
500			let mut vm = Vm::from_services(symbols, &self.0, params, tx.identity());
501			let start_execute = self.0.runtime_context.clock.instant();
502			let run_result = vm.run(&self.0, &mut tx, &compiled.instructions, &mut result);
503			let execute_duration = start_execute.elapsed();
504			symbols = vm.symbols;
505
506			metrics.push(StatementMetric {
507				fingerprint: compiled.fingerprint,
508				normalized_rql: compiled.normalized_rql,
509				compile_duration_us: compile_duration.as_micros() as u64,
510				execute_duration_us: execute_duration.as_micros() as u64,
511				rows_affected: if run_result.is_ok() {
512					extract_rows_affected(&result)
513				} else {
514					0
515				},
516			});
517
518			if let Err(e) = run_result {
519				return error_result(e, build_metrics(metrics));
520			}
521
522			if compiled.is_output {
523				output_results.append(&mut result);
524			}
525		}
526		ExecutionResult {
527			frames: merge_results(output_results, result),
528			error: None,
529			metrics: build_metrics(metrics),
530		}
531	}
532
533	#[instrument(name = "executor::test", level = "debug", skip(self, txn, cmd), fields(rql = %cmd.rql))]
534	pub fn test(&self, txn: &mut TestTransaction<'_>, cmd: Test<'_>) -> ExecutionResult {
535		let symbols = match self.setup_symbols(&cmd.params, &mut Transaction::Test(Box::new(txn.reborrow()))) {
536			Ok(s) => s,
537			Err(e) => return error_result(e, ExecutionMetrics::default()),
538		};
539		if let Err(e) = self.enforce_test_policy(&symbols, txn) {
540			return error_result(e, ExecutionMetrics::default());
541		}
542		let start_compile = self.0.runtime_context.clock.instant();
543		match self.compiler.compile_with_policy(
544			&mut Transaction::Test(Box::new(txn.reborrow())),
545			cmd.rql,
546			inject_from_policies,
547		) {
548			Err(err) => self.handle_test_compile_error(err, cmd.rql, cmd.params),
549			Ok(CompilationResult::Ready(compiled)) => {
550				self.execute_test_ready(txn, compiled, &cmd.params, symbols, start_compile)
551			}
552			Ok(CompilationResult::Incremental(state)) => {
553				self.execute_test_incremental(txn, state, &cmd.params, symbols)
554			}
555		}
556	}
557
558	#[inline]
559	fn enforce_test_policy(&self, symbols: &SymbolTable, txn: &mut TestTransaction<'_>) -> Result<()> {
560		let session_type = txn.session_type;
561		let session_default_deny = txn.session_default_deny;
562		PolicyEvaluator::new(&self.0, symbols).enforce_session_policy(
563			&mut Transaction::Test(Box::new(txn.reborrow())),
564			session_type,
565			session_default_deny,
566		)
567	}
568
569	#[inline]
570	#[cfg_attr(reifydb_single_threaded, allow(unused_variables))]
571	fn handle_test_compile_error(&self, err: Error, rql: &str, params: Params) -> ExecutionResult {
572		#[cfg(not(reifydb_single_threaded))]
573		if let Ok(Some(frames)) = self.try_forward_remote_query(&err, rql, params) {
574			return ExecutionResult {
575				frames,
576				error: None,
577				metrics: ExecutionMetrics::default(),
578			};
579		}
580		error_result(err, ExecutionMetrics::default())
581	}
582
583	#[inline]
584	fn execute_test_ready(
585		&self,
586		txn: &mut TestTransaction<'_>,
587		compiled: Arc<Vec<Compiled>>,
588		params: &Params,
589		symbols: SymbolTable,
590		start_compile: Instant,
591	) -> ExecutionResult {
592		let compile_duration = Duration::from_std(start_compile.elapsed());
593		match execute_compiled_units(
594			&self.0,
595			&mut Transaction::Test(Box::new(txn.reborrow())),
596			&compiled,
597			params,
598			symbols,
599			compile_duration,
600		) {
601			Ok((output, remaining, _, metrics)) => ExecutionResult {
602				frames: merge_results(output, remaining),
603				error: None,
604				metrics: build_metrics(metrics),
605			},
606			Err(f) => ExecutionResult {
607				frames: vec![],
608				error: Some(f.error),
609				metrics: build_metrics(f.partial_metrics),
610			},
611		}
612	}
613
614	fn execute_test_incremental(
615		&self,
616		txn: &mut TestTransaction<'_>,
617		mut state: IncrementalCompilation,
618		params: &Params,
619		symbols: SymbolTable,
620	) -> ExecutionResult {
621		let policy = constrain_policy(inject_from_policies);
622		let mut result = vec![];
623		let mut output_results: Vec<Frame> = Vec::new();
624		let mut symbols = symbols;
625		let mut metrics = Vec::new();
626		loop {
627			let start_incr = self.0.runtime_context.clock.instant();
628			let next = match self.compiler.compile_next_with_policy(
629				&mut Transaction::Test(Box::new(txn.reborrow())),
630				&mut state,
631				&policy,
632			) {
633				Ok(n) => n,
634				Err(e) => return error_result(e, build_metrics(metrics)),
635			};
636			let compile_duration = start_incr.elapsed();
637
638			let Some(compiled) = next else {
639				break;
640			};
641
642			result.clear();
643			let mut tx = Transaction::Test(Box::new(txn.reborrow()));
644			let mut vm = Vm::from_services(symbols, &self.0, params, tx.identity());
645			let start_execute = self.0.runtime_context.clock.instant();
646			let run_result = vm.run(&self.0, &mut tx, &compiled.instructions, &mut result);
647			let execute_duration = start_execute.elapsed();
648			symbols = vm.symbols;
649
650			metrics.push(StatementMetric {
651				fingerprint: compiled.fingerprint,
652				normalized_rql: compiled.normalized_rql,
653				compile_duration_us: compile_duration.as_micros() as u64,
654				execute_duration_us: execute_duration.as_micros() as u64,
655				rows_affected: if run_result.is_ok() {
656					extract_rows_affected(&result)
657				} else {
658					0
659				},
660			});
661
662			if let Err(e) = run_result {
663				return error_result(e, build_metrics(metrics));
664			}
665
666			if compiled.is_output {
667				output_results.append(&mut result);
668			}
669		}
670		ExecutionResult {
671			frames: merge_results(output_results, result),
672			error: None,
673			metrics: build_metrics(metrics),
674		}
675	}
676
677	#[instrument(name = "executor::subscription", level = "debug", skip(self, txn, cmd), fields(rql = %cmd.rql))]
678	pub fn subscription(&self, txn: &mut QueryTransaction, cmd: Subscription<'_>) -> ExecutionResult {
679		let bump = Bump::new();
680		let statements = match parse_str(&bump, cmd.rql) {
681			Ok(s) => s,
682			Err(e) => {
683				return ExecutionResult {
684					frames: vec![],
685					error: Some(e),
686					metrics: ExecutionMetrics::default(),
687				};
688			}
689		};
690
691		if statements.len() != 1 {
692			return ExecutionResult {
693				frames: vec![],
694				error: Some(Error(Box::new(subscription::single_statement_required(
695					"Subscription endpoint requires exactly one statement",
696				)))),
697				metrics: ExecutionMetrics::default(),
698			};
699		}
700
701		let statement = &statements[0];
702		if statement.nodes.len() != 1 || !statement.nodes[0].is_subscription_ddl() {
703			return ExecutionResult {
704				frames: vec![],
705				error: Some(Error(Box::new(subscription::invalid_statement(
706					"Subscription endpoint only supports CREATE SUBSCRIPTION or DROP SUBSCRIPTION",
707				)))),
708				metrics: ExecutionMetrics::default(),
709			};
710		}
711
712		let symbols = match self.setup_symbols(&cmd.params, &mut Transaction::Query(&mut *txn)) {
713			Ok(s) => s,
714			Err(e) => {
715				return ExecutionResult {
716					frames: vec![],
717					error: Some(e),
718					metrics: ExecutionMetrics::default(),
719				};
720			}
721		};
722
723		if let Err(e) = PolicyEvaluator::new(&self.0, &symbols).enforce_session_policy(
724			&mut Transaction::Query(&mut *txn),
725			SessionOp::Subscription,
726			true,
727		) {
728			return ExecutionResult {
729				frames: vec![],
730				error: Some(e),
731				metrics: ExecutionMetrics::default(),
732			};
733		}
734
735		let start_compile = self.0.runtime_context.clock.instant();
736		let compiled = match self.compiler.compile_with_policy(
737			&mut Transaction::Query(txn),
738			cmd.rql,
739			inject_from_policies,
740		) {
741			Ok(CompilationResult::Ready(compiled)) => compiled,
742			Ok(CompilationResult::Incremental(_)) => {
743				unreachable!("Single subscription statement should not require incremental compilation")
744			}
745			Err(err) => {
746				return ExecutionResult {
747					frames: vec![],
748					error: Some(err),
749					metrics: ExecutionMetrics::default(),
750				};
751			}
752		};
753		let compile_duration = Duration::from_std(start_compile.elapsed());
754
755		match execute_compiled_units(
756			&self.0,
757			&mut Transaction::Query(txn),
758			&compiled,
759			&cmd.params,
760			symbols,
761			compile_duration,
762		) {
763			Ok((output, remaining, _, metrics)) => ExecutionResult {
764				frames: merge_results(output, remaining),
765				error: None,
766				metrics: build_metrics(metrics),
767			},
768			Err(f) => ExecutionResult {
769				frames: vec![],
770				error: Some(f.error),
771				metrics: build_metrics(f.partial_metrics),
772			},
773		}
774	}
775
776	#[instrument(name = "executor::command", level = "debug", skip(self, txn, cmd), fields(rql = %cmd.rql))]
777	pub fn command(&self, txn: &mut CommandTransaction, cmd: Command<'_>) -> ExecutionResult {
778		let symbols = match self.setup_symbols(&cmd.params, &mut Transaction::Command(&mut *txn)) {
779			Ok(s) => s,
780			Err(e) => {
781				return ExecutionResult {
782					frames: vec![],
783					error: Some(e),
784					metrics: ExecutionMetrics::default(),
785				};
786			}
787		};
788
789		if let Err(e) = PolicyEvaluator::new(&self.0, &symbols).enforce_session_policy(
790			&mut Transaction::Command(&mut *txn),
791			SessionOp::Command,
792			false,
793		) {
794			return ExecutionResult {
795				frames: vec![],
796				error: Some(e),
797				metrics: ExecutionMetrics::default(),
798			};
799		}
800
801		let start_compile = self.0.runtime_context.clock.instant();
802		let compiled = match self.compile_query(&mut Transaction::Command(txn), cmd.rql) {
803			Ok(CompilationResult::Ready(compiled)) => compiled,
804			Ok(CompilationResult::Incremental(_)) => {
805				unreachable!("DDL statements require admin transactions, not command transactions")
806			}
807			Err(err) => {
808				#[cfg(not(reifydb_single_threaded))]
809				if self.0.remote_registry.is_some() && remote::is_remote_query(&err) {
810					return ExecutionResult {
811						frames: vec![],
812						error: Some(Error(Box::new(Diagnostic {
813							code: "REMOTE_002".to_string(),
814							message: "Write operations on remote namespaces are not supported"
815								.to_string(),
816							help: Some("Use the remote instance directly for write operations"
817								.to_string()),
818							..Default::default()
819						}))),
820						metrics: ExecutionMetrics::default(),
821					};
822				}
823				return ExecutionResult {
824					frames: vec![],
825					error: Some(err),
826					metrics: ExecutionMetrics::default(),
827				};
828			}
829		};
830		let compile_duration = Duration::from_std(start_compile.elapsed());
831
832		match execute_compiled_units(
833			&self.0,
834			&mut Transaction::Command(txn),
835			&compiled,
836			&cmd.params,
837			symbols,
838			compile_duration,
839		) {
840			Ok((output, remaining, _, metrics)) => ExecutionResult {
841				frames: merge_results(output, remaining),
842				error: None,
843				metrics: build_metrics(metrics),
844			},
845			Err(f) => ExecutionResult {
846				frames: vec![],
847				error: Some(f.error),
848				metrics: build_metrics(f.partial_metrics),
849			},
850		}
851	}
852
853	#[instrument(name = "executor::call_procedure", level = "debug", skip(self, txn, params), fields(name = %name))]
854	pub fn call_procedure(&self, txn: &mut CommandTransaction, name: &str, params: &Params) -> ExecutionResult {
855		let rql = format!("CALL {}()", name);
856		let symbols = match self.setup_symbols(params, &mut Transaction::Command(&mut *txn)) {
857			Ok(s) => s,
858			Err(e) => return error_result(e, ExecutionMetrics::default()),
859		};
860
861		let start_compile = self.0.runtime_context.clock.instant();
862		let compiled = match self.compiler.compile(&mut Transaction::Command(txn), &rql) {
863			Ok(CompilationResult::Ready(compiled)) => compiled,
864			Ok(CompilationResult::Incremental(_)) => {
865				unreachable!("CALL statements should not require incremental compilation")
866			}
867			Err(e) => return error_result(e, ExecutionMetrics::default()),
868		};
869		let compile_duration = Duration::from_std(start_compile.elapsed());
870
871		match self.run_command_units_collecting_last(txn, &compiled, params, symbols, compile_duration) {
872			Ok((frames, metrics)) => ExecutionResult {
873				frames,
874				error: None,
875				metrics: build_metrics(metrics),
876			},
877			Err(f) => error_result(f.error, build_metrics(f.partial_metrics)),
878		}
879	}
880
881	#[inline]
882	fn run_command_units_collecting_last(
883		&self,
884		txn: &mut CommandTransaction,
885		compiled: &[Compiled],
886		params: &Params,
887		mut symbols: SymbolTable,
888		compile_duration: Duration,
889	) -> StdResult<(Vec<Frame>, Vec<StatementMetric>), ExecutionFailure> {
890		let compile_duration_us = compile_duration.to_std().as_micros() as u64 / compiled.len().max(1) as u64;
891		let mut result = vec![];
892		let mut metrics = Vec::new();
893		for compiled in compiled.iter() {
894			result.clear();
895			let mut tx = Transaction::Command(txn);
896			let mut vm = Vm::from_services(symbols, &self.0, params, tx.identity());
897			let start_execute = self.0.runtime_context.clock.instant();
898			let run_result = vm.run(&self.0, &mut tx, &compiled.instructions, &mut result);
899			let execute_duration = start_execute.elapsed();
900			symbols = vm.symbols;
901
902			metrics.push(StatementMetric {
903				fingerprint: compiled.fingerprint,
904				normalized_rql: compiled.normalized_rql.clone(),
905				compile_duration_us,
906				execute_duration_us: execute_duration.as_micros() as u64,
907				rows_affected: if run_result.is_ok() {
908					extract_rows_affected(&result)
909				} else {
910					0
911				},
912			});
913
914			if let Err(error) = run_result {
915				return Err(ExecutionFailure {
916					error,
917					partial_metrics: metrics,
918				});
919			}
920		}
921
922		Ok((result, metrics))
923	}
924
925	#[instrument(name = "executor::query", level = "debug", skip(self, txn, qry), fields(rql = %qry.rql))]
926	pub fn query(&self, txn: &mut QueryTransaction, qry: Query<'_>) -> ExecutionResult {
927		let symbols = match self.setup_symbols(&qry.params, &mut Transaction::Query(&mut *txn)) {
928			Ok(s) => s,
929			Err(e) => {
930				return ExecutionResult {
931					frames: vec![],
932					error: Some(e),
933					metrics: ExecutionMetrics::default(),
934				};
935			}
936		};
937
938		if let Err(e) = PolicyEvaluator::new(&self.0, &symbols).enforce_session_policy(
939			&mut Transaction::Query(&mut *txn),
940			SessionOp::Query,
941			false,
942		) {
943			return ExecutionResult {
944				frames: vec![],
945				error: Some(e),
946				metrics: ExecutionMetrics::default(),
947			};
948		}
949
950		let start_compile = self.0.runtime_context.clock.instant();
951		let compiled = match self.compile_query(&mut Transaction::Query(txn), qry.rql) {
952			Ok(CompilationResult::Ready(compiled)) => compiled,
953			Ok(CompilationResult::Incremental(_)) => {
954				unreachable!("DDL statements require admin transactions, not query transactions")
955			}
956			Err(err) => {
957				#[cfg(not(reifydb_single_threaded))]
958				if let Ok(Some(frames)) = self.try_forward_remote_query(&err, qry.rql, qry.params) {
959					return ExecutionResult {
960						frames,
961						error: None,
962						metrics: ExecutionMetrics::default(),
963					};
964				}
965				return ExecutionResult {
966					frames: vec![],
967					error: Some(err),
968					metrics: ExecutionMetrics::default(),
969				};
970			}
971		};
972		let compile_duration = Duration::from_std(start_compile.elapsed());
973
974		let exec_result = execute_compiled_units(
975			&self.0,
976			&mut Transaction::Query(txn),
977			&compiled,
978			&qry.params,
979			symbols,
980			compile_duration,
981		);
982
983		match exec_result {
984			Ok((output, remaining, _, metrics)) => ExecutionResult {
985				frames: merge_results(output, remaining),
986				error: None,
987				metrics: build_metrics(metrics),
988			},
989			Err(f) => ExecutionResult {
990				frames: vec![],
991				error: Some(f.error),
992				metrics: build_metrics(f.partial_metrics),
993			},
994		}
995	}
996}