alopex_sql/executor/evaluator/
context.rs1use std::cell::RefCell;
2use std::time::{SystemTime, UNIX_EPOCH};
3
4use crate::executor::{EvaluationError, ExecutorError};
5use crate::storage::SqlValue;
6
7thread_local! {
8 static STATEMENT_TIMESTAMPS: RefCell<Vec<i64>> = const { RefCell::new(Vec::new()) };
9}
10
11pub(crate) struct StatementTimestampGuard {
13 active: bool,
14}
15
16pub(crate) fn begin_statement() -> StatementTimestampGuard {
21 let active = STATEMENT_TIMESTAMPS.with(|timestamps| {
22 let mut timestamps = timestamps.borrow_mut();
23 if timestamps.is_empty() {
24 timestamps.push(utc_now_micros());
25 true
26 } else {
27 false
28 }
29 });
30 StatementTimestampGuard { active }
31}
32
33impl Drop for StatementTimestampGuard {
34 fn drop(&mut self) {
35 if self.active {
36 STATEMENT_TIMESTAMPS.with(|timestamps| {
37 timestamps.borrow_mut().pop();
38 });
39 }
40 }
41}
42
43pub(crate) fn current_statement_timestamp() -> i64 {
44 STATEMENT_TIMESTAMPS.with(|timestamps| {
45 timestamps
46 .borrow()
47 .last()
48 .copied()
49 .unwrap_or_else(utc_now_micros)
50 })
51}
52
53fn utc_now_micros() -> i64 {
54 match SystemTime::now().duration_since(UNIX_EPOCH) {
55 Ok(duration) => i64::try_from(duration.as_micros()).unwrap_or(i64::MAX),
56 Err(error) => -i64::try_from(error.duration().as_micros()).unwrap_or(i64::MAX),
57 }
58}
59
60pub struct EvalContext<'a> {
62 row: &'a [SqlValue],
63 statement_timestamp: i64,
64}
65
66impl<'a> EvalContext<'a> {
67 pub fn new(row: &'a [SqlValue]) -> Self {
69 Self {
70 row,
71 statement_timestamp: current_statement_timestamp(),
72 }
73 }
74
75 pub fn get(&self, index: usize) -> Result<&'a SqlValue, ExecutorError> {
77 self.row.get(index).ok_or(ExecutorError::Evaluation(
78 EvaluationError::InvalidColumnRef { index },
79 ))
80 }
81
82 pub(crate) fn statement_timestamp(&self) -> i64 {
84 self.statement_timestamp
85 }
86}
87
88#[cfg(test)]
89mod tests {
90 use super::*;
91
92 #[test]
93 fn get_existing_column_returns_value() {
94 let row = vec![SqlValue::Integer(1), SqlValue::Text("a".into())];
95 let ctx = EvalContext::new(&row);
96 assert!(matches!(ctx.get(0), Ok(SqlValue::Integer(1))));
97 }
98
99 #[test]
100 fn get_out_of_range_errors() {
101 let row = vec![SqlValue::Integer(1)];
102 let ctx = EvalContext::new(&row);
103 let err = ctx.get(2).unwrap_err();
104 match err {
105 ExecutorError::Evaluation(EvaluationError::InvalidColumnRef { index }) => {
106 assert_eq!(index, 2)
107 }
108 other => panic!("unexpected error {other:?}"),
109 }
110 }
111}