surrealdb_sql/ctx/
context.rs1use crate::ctx::canceller::Canceller;
2use crate::ctx::reason::Reason;
3use crate::dbs::capabilities::FuncTarget;
4#[cfg(feature = "http")]
5use crate::dbs::capabilities::NetTarget;
6use crate::dbs::{Capabilities, Notification};
7use crate::err::Error;
8use crate::idx::planner::QueryPlanner;
9use crate::value::Value;
10use channel::Sender;
11use std::borrow::Cow;
12use std::collections::HashMap;
13use std::fmt::{self, Debug};
14use std::str::FromStr;
15use std::sync::atomic::{AtomicBool, Ordering};
16use std::sync::Arc;
17use std::time::Duration;
18use trice::Instant;
19#[cfg(feature = "http")]
20use url::Url;
21
22impl<'a> From<Value> for Cow<'a, Value> {
23 fn from(v: Value) -> Cow<'a, Value> {
24 Cow::Owned(v)
25 }
26}
27
28impl<'a> From<&'a Value> for Cow<'a, Value> {
29 fn from(v: &'a Value) -> Cow<'a, Value> {
30 Cow::Borrowed(v)
31 }
32}
33pub struct Context<'a> {
34 parent: Option<&'a Context<'a>>,
36 deadline: Option<Instant>,
38 cancelled: Arc<AtomicBool>,
40 values: HashMap<Cow<'static, str>, Cow<'a, Value>>,
42 notifications: Option<Sender<Notification>>,
44 query_planner: Option<&'a QueryPlanner<'a>>,
46 capabilities: Arc<Capabilities>,
48}
49
50impl<'a> Default for Context<'a> {
51 fn default() -> Self {
52 Context::background()
53 }
54}
55
56impl<'a> Debug for Context<'a> {
57 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
58 f.debug_struct("Context")
59 .field("parent", &self.parent)
60 .field("deadline", &self.deadline)
61 .field("cancelled", &self.cancelled)
62 .field("values", &self.values)
63 .finish()
64 }
65}
66
67impl<'a> Context<'a> {
68 pub fn background() -> Self {
70 Context {
71 values: HashMap::default(),
72 parent: None,
73 deadline: None,
74 cancelled: Arc::new(AtomicBool::new(false)),
75 notifications: None,
76 query_planner: None,
77 capabilities: Arc::new(Capabilities::default()),
78 }
79 }
80
81 pub fn new(parent: &'a Context) -> Self {
83 Context {
84 values: HashMap::default(),
85 parent: Some(parent),
86 deadline: parent.deadline,
87 cancelled: Arc::new(AtomicBool::new(false)),
88 notifications: parent.notifications.clone(),
89 query_planner: parent.query_planner,
90 capabilities: parent.capabilities.clone(),
91 }
92 }
93
94 pub fn add_value<K, V>(&mut self, key: K, value: V)
97 where
98 K: Into<Cow<'static, str>>,
99 V: Into<Cow<'a, Value>>,
100 {
101 self.values.insert(key.into(), value.into());
102 }
103
104 pub fn add_cancel(&mut self) -> Canceller {
107 let cancelled = self.cancelled.clone();
108 Canceller::new(cancelled)
109 }
110
111 pub fn add_deadline(&mut self, deadline: Instant) {
114 match self.deadline {
115 Some(current) if current < deadline => (),
116 _ => self.deadline = Some(deadline),
117 }
118 }
119
120 pub fn add_timeout(&mut self, timeout: Duration) -> Result<(), Error> {
124 match Instant::now().checked_add(timeout) {
125 Some(deadline) => {
126 self.add_deadline(deadline);
127 Ok(())
128 }
129 None => Err(Error::InvalidTimeout(timeout.as_secs())),
130 }
131 }
132
133 pub fn add_notifications(&mut self, chn: Option<&Sender<Notification>>) {
136 self.notifications = chn.cloned()
137 }
138
139 pub(crate) fn set_query_planner(&mut self, qp: &'a QueryPlanner) {
141 self.query_planner = Some(qp);
142 }
143
144 pub fn timeout(&self) -> Option<Duration> {
147 self.deadline.map(|v| v.saturating_duration_since(Instant::now()))
148 }
149
150 pub fn notifications(&self) -> Option<Sender<Notification>> {
151 self.notifications.clone()
152 }
153
154 pub(crate) fn get_query_planner(&self) -> Option<&QueryPlanner> {
155 self.query_planner
156 }
157
158 pub fn done(&self) -> Option<Reason> {
161 match self.deadline {
162 Some(deadline) if deadline <= Instant::now() => Some(Reason::Timedout),
163 _ if self.cancelled.load(Ordering::Relaxed) => Some(Reason::Canceled),
164 _ => match self.parent {
165 Some(ctx) => ctx.done(),
166 _ => None,
167 },
168 }
169 }
170
171 pub fn is_ok(&self) -> bool {
173 self.done().is_none()
174 }
175
176 pub fn is_done(&self) -> bool {
178 self.done().is_some()
179 }
180
181 pub fn is_timedout(&self) -> bool {
183 matches!(self.done(), Some(Reason::Timedout))
184 }
185
186 pub fn value(&self, key: &str) -> Option<&Value> {
189 match self.values.get(key) {
190 Some(v) => match v {
191 Cow::Borrowed(v) => Some(*v),
192 Cow::Owned(v) => Some(v),
193 },
194 None => match self.parent {
195 Some(p) => p.value(key),
196 _ => None,
197 },
198 }
199 }
200
201 #[cfg(feature = "scripting")]
203 pub fn cancellation(&self) -> crate::ctx::cancellation::Cancellation {
204 crate::ctx::cancellation::Cancellation::new(
205 self.deadline,
206 std::iter::successors(Some(self), |ctx| ctx.parent)
207 .map(|ctx| ctx.cancelled.clone())
208 .collect(),
209 )
210 }
211
212 pub fn add_capabilities(&mut self, caps: Capabilities) {
218 self.capabilities = Arc::new(caps);
219 }
220
221 #[allow(dead_code)]
223 pub fn get_capabilities(&self) -> Arc<Capabilities> {
224 self.capabilities.clone()
225 }
226
227 #[allow(dead_code)]
229 pub fn check_allowed_scripting(&self) -> Result<(), Error> {
230 if !self.capabilities.allows_scripting() {
231 return Err(Error::ScriptingNotAllowed);
232 }
233 Ok(())
234 }
235
236 pub fn check_allowed_function(&self, target: &str) -> Result<(), Error> {
238 let func_target = FuncTarget::from_str(target).map_err(|_| Error::InvalidFunction {
239 name: target.to_string(),
240 message: "Invalid function name".to_string(),
241 })?;
242
243 if !self.capabilities.allows_function(&func_target) {
244 return Err(Error::FunctionNotAllowed(target.to_string()));
245 }
246 Ok(())
247 }
248
249 #[cfg(feature = "http")]
251 pub fn check_allowed_net(&self, target: &Url) -> Result<(), Error> {
252 match target.host() {
253 Some(host)
254 if self.capabilities.allows_network_target(&NetTarget::Host(
255 host.to_owned(),
256 target.port_or_known_default(),
257 )) =>
258 {
259 Ok(())
260 }
261 _ => Err(Error::NetTargetNotAllowed(target.to_string())),
262 }
263 }
264}