radixdb_executor/access/
handle.rs1use std::sync::{Arc, Mutex};
4
5use radixdb_core::{Error, Result};
6use radixdb_storage::mvcc::engine::MVCCEngine;
7use radixdb_storage::traits::{Engine, Table, Transaction};
8
9use crate::mutation::host::ActiveTransaction;
10
11pub struct QueryTableHandle {
14 pub table: Box<dyn Table>,
15 pub in_explicit_transaction: bool,
16 _statement_transaction: Option<Box<dyn Transaction>>,
17}
18
19pub struct QueryTablePairHandle {
20 pub left: Box<dyn Table>,
21 pub right: Box<dyn Table>,
22 pub in_explicit_transaction: bool,
23 pub statement_transaction: Option<Box<dyn Transaction>>,
24}
25
26impl QueryTableHandle {
27 pub fn into_parts(self) -> (Box<dyn Table>, Option<Box<dyn Transaction>>, bool) {
28 (
29 self.table,
30 self._statement_transaction,
31 self.in_explicit_transaction,
32 )
33 }
34}
35
36pub enum QuerySourceHandle {
37 Current(QueryTableHandle),
38 Temporal(Box<dyn Transaction>),
39}
40
41pub fn open_query_table(
44 engine: &Arc<MVCCEngine>,
45 active_transaction: &Mutex<Option<ActiveTransaction>>,
46 table_name: &str,
47) -> Result<QueryTableHandle> {
48 let active = active_transaction.lock().unwrap();
49 if let Some(state) = active.as_ref() {
50 let table = map_table_not_found(state.transaction.get_table(table_name), table_name)?;
51 drop(active);
52 return Ok(QueryTableHandle {
53 table,
54 in_explicit_transaction: true,
55 _statement_transaction: None,
56 });
57 }
58 drop(active);
59
60 let transaction = engine.begin_transaction()?;
61 let table = map_table_not_found(transaction.get_table(table_name), table_name)?;
62 Ok(QueryTableHandle {
63 table,
64 in_explicit_transaction: false,
65 _statement_transaction: Some(transaction),
66 })
67}
68
69pub fn open_query_table_raw(
72 engine: &Arc<MVCCEngine>,
73 active_transaction: &Mutex<Option<ActiveTransaction>>,
74 table_name: &str,
75) -> Result<QueryTableHandle> {
76 let active = active_transaction.lock().unwrap();
77 if let Some(state) = active.as_ref() {
78 let table = state.transaction.get_table(table_name)?;
79 drop(active);
80 return Ok(QueryTableHandle {
81 table,
82 in_explicit_transaction: true,
83 _statement_transaction: None,
84 });
85 }
86 drop(active);
87 let transaction = engine.begin_transaction()?;
88 let table = transaction.get_table(table_name)?;
89 Ok(QueryTableHandle {
90 table,
91 in_explicit_transaction: false,
92 _statement_transaction: Some(transaction),
93 })
94}
95
96pub fn open_query_table_pair(
98 engine: &Arc<MVCCEngine>,
99 active_transaction: &Mutex<Option<ActiveTransaction>>,
100 left_name: &str,
101 right_name: &str,
102) -> Result<QueryTablePairHandle> {
103 let active = active_transaction.lock().unwrap();
104 if let Some(state) = active.as_ref() {
105 let left = state.transaction.get_table(left_name)?;
106 let right = state.transaction.get_table(right_name)?;
107 drop(active);
108 return Ok(QueryTablePairHandle {
109 left,
110 right,
111 in_explicit_transaction: true,
112 statement_transaction: None,
113 });
114 }
115 drop(active);
116 let transaction = engine.begin_transaction()?;
117 let left = transaction.get_table(left_name)?;
118 let right = transaction.get_table(right_name)?;
119 Ok(QueryTablePairHandle {
120 left,
121 right,
122 in_explicit_transaction: false,
123 statement_transaction: Some(transaction),
124 })
125}
126
127pub fn query_table_has_cold_segments(
129 engine: &Arc<MVCCEngine>,
130 active_transaction: &Mutex<Option<ActiveTransaction>>,
131 table_name: &str,
132) -> bool {
133 open_query_table_raw(engine, active_transaction, table_name)
134 .is_ok_and(|handle| handle.table.has_cold_segments())
135}
136
137pub fn open_query_source(
140 engine: &Arc<MVCCEngine>,
141 active_transaction: &Mutex<Option<ActiveTransaction>>,
142 table_name: &str,
143 temporal_requested: bool,
144) -> Result<QuerySourceHandle> {
145 let active = active_transaction.lock().unwrap();
146 if let Some(state) = active.as_ref() {
147 let table = map_table_not_found(state.transaction.get_table(table_name), table_name)?;
148 drop(active);
149 return Ok(QuerySourceHandle::Current(QueryTableHandle {
150 table,
151 in_explicit_transaction: true,
152 _statement_transaction: None,
153 }));
154 }
155 drop(active);
156
157 let transaction = engine.begin_transaction()?;
158 if temporal_requested {
159 return Ok(QuerySourceHandle::Temporal(transaction));
160 }
161 let table = map_table_not_found(transaction.get_table(table_name), table_name)?;
162 Ok(QuerySourceHandle::Current(QueryTableHandle {
163 table,
164 in_explicit_transaction: false,
165 _statement_transaction: Some(transaction),
166 }))
167}
168
169pub fn begin_query_transaction(engine: &Arc<MVCCEngine>) -> Result<Box<dyn Transaction>> {
171 engine.begin_transaction()
172}
173
174fn map_table_not_found<T>(result: Result<T>, table_name: &str) -> Result<T> {
175 result.map_err(|error| {
176 if matches!(error, Error::TableNotFound(_)) {
177 Error::TableOrViewNotFound(table_name.to_string())
178 } else {
179 error
180 }
181 })
182}
183
184#[cfg(test)]
185mod tests {
186 use super::*;
187 use radixdb_storage::Config;
188
189 #[test]
190 fn missing_table_uses_public_table_or_view_error() {
191 let engine = Arc::new(MVCCEngine::new(Config::in_memory()));
192 engine.open_engine().unwrap();
193 let active = Mutex::new(None);
194 let error = match open_query_table(&engine, &active, "missing") {
195 Ok(_) => panic!("missing table unexpectedly opened"),
196 Err(error) => error,
197 };
198 assert!(
199 matches!(error, Error::TableOrViewNotFound(ref name) if name == "missing"),
200 "unexpected error: {error:?}"
201 );
202 }
203}