Skip to main content

shape_runtime/context/
data_access.rs

1//! Data row access methods for ExecutionContext
2//!
3//! Handles retrieving data rows by index, range, and timeframe.
4
5use crate::data::OwnedDataRow as RowValue;
6use crate::data::Timeframe;
7use shape_ast::error::{Result, ShapeError};
8
9impl super::ExecutionContext {
10    /// Set the current data row index
11    pub fn set_current_row(&mut self, index: usize) -> Result<()> {
12        // With DuckDB, we don't track current row index the same way
13        self.current_row_index = index;
14        Ok(())
15    }
16
17    /// Get the current data row index
18    pub fn current_row_index(&self) -> usize {
19        self.current_row_index
20    }
21
22    /// Get the current data row index (alias for compatibility)
23    pub fn get_current_row_index(&self) -> usize {
24        self.current_row_index
25    }
26
27    /// Get total row count
28    pub fn row_count(&self) -> usize {
29        if let Some(ref cache) = self.data_cache {
30            if let (Ok(id), Ok(timeframe)) = (self.get_current_id(), self.get_current_timeframe()) {
31                return cache.row_count(&id, &timeframe);
32            }
33        }
34        0
35    }
36
37    /// Get timestamp for a row index
38    pub fn get_row_timestamp(&self, index: usize) -> Result<i64> {
39        if let Some(ref cache) = self.data_cache {
40            let id = self.get_current_id()?;
41            let timeframe = self.get_current_timeframe()?;
42
43            if let Some(row) = cache.get_row(&id, &timeframe, index) {
44                return Ok(row.timestamp);
45            }
46        }
47
48        Err(ShapeError::DataError {
49            message: format!("No timestamp available at index {}", index),
50            symbol: self.current_id.clone(),
51            timeframe: self.current_timeframe.as_ref().map(|t| t.to_string()),
52        })
53    }
54
55    /// Get a data row by relative index
56    pub fn get_row(&mut self, relative_index: i32) -> Result<RowValue> {
57        tracing::trace!("get_row called with relative_index: {}", relative_index);
58
59        // Check look-ahead bias
60        if let Some(ref guard) = self.lookahead_guard {
61            guard.check_row_index(relative_index, "get_row")?;
62        }
63
64        // Phase 6: Check data_cache first (sync access to prefetched data)
65        if let Some(ref cache) = self.data_cache {
66            let id = self.get_current_id()?;
67            let timeframe = self.get_current_timeframe()?;
68            let absolute_index = (self.current_row_index as i32 + relative_index) as usize;
69
70            if let Some(row) = cache.get_row(&id, &timeframe, absolute_index) {
71                return Ok(row);
72            }
73        }
74
75        // No data available - this is an error
76        // With the async architecture, data must be prefetched before execution
77        Err(ShapeError::DataError {
78            message: format!(
79                "No data available at index {}. Data must be prefetched before execution.",
80                relative_index
81            ),
82            symbol: self.current_id.clone(), // Field name in ShapeError is still 'symbol' for compatibility
83            timeframe: self.current_timeframe.as_ref().map(|tf| tf.to_string()),
84        })
85    }
86
87    /// Get data rows in a range
88    pub fn get_row_range(&mut self, start: i32, end: i32) -> Result<Vec<RowValue>> {
89        // Check look-ahead bias for range access
90        if let Some(ref guard) = self.lookahead_guard {
91            // Check both start and end indices
92            guard.check_row_index(start, "get_row_range_start")?;
93            guard.check_row_index(end, "get_row_range_end")?;
94        }
95
96        if start > end {
97            return Err(ShapeError::RuntimeError {
98                message: format!("Invalid row range: {} to {}", start, end),
99                location: None,
100            });
101        }
102
103        // Phase 6: Check data_cache first (sync access to prefetched data)
104        if let Some(ref cache) = self.data_cache {
105            let id = self.get_current_id()?;
106            let timeframe = self.get_current_timeframe()?;
107
108            let mut rows = Vec::new();
109            for i in start..=end {
110                let absolute_index = (self.current_row_index as i32 + i) as usize;
111                if let Some(row) = cache.get_row(&id, &timeframe, absolute_index) {
112                    rows.push(row);
113                }
114            }
115            return Ok(rows);
116        }
117
118        Ok(Vec::new())
119    }
120
121    /// Get a data row with explicit timeframe
122    pub fn get_row_with_timeframe(
123        &mut self,
124        index: i32,
125        timeframe: &Timeframe,
126    ) -> Result<RowValue> {
127        // Phase 6: Check data_cache first
128        if let Some(ref cache) = self.data_cache {
129            let id = self.get_current_id()?;
130            let absolute_index = (self.current_row_index as i32 + index) as usize;
131
132            if let Some(row) = cache.get_row(&id, timeframe, absolute_index) {
133                return Ok(row);
134            }
135        }
136
137        Err(ShapeError::DataError {
138            message: format!(
139                "No data available for timeframe {} at index {}",
140                timeframe, index
141            ),
142            symbol: self.current_id.clone(),
143            timeframe: Some(timeframe.to_string()),
144        })
145    }
146
147    /// Get data rows in a range with explicit timeframe
148    pub fn get_row_range_with_timeframe(
149        &mut self,
150        start: i32,
151        end: i32,
152        timeframe: &Timeframe,
153    ) -> Result<Vec<RowValue>> {
154        if start > end {
155            return Err(ShapeError::RuntimeError {
156                message: format!("Invalid row range: {} to {}", start, end),
157                location: None,
158            });
159        }
160
161        if let Some(ref cache) = self.data_cache {
162            let id = self.get_current_id()?;
163
164            let mut rows = Vec::new();
165            for i in start..=end {
166                let absolute_index = (self.current_row_index as i32 + i) as usize;
167                if let Some(row) = cache.get_row(&id, timeframe, absolute_index) {
168                    rows.push(row);
169                }
170            }
171            return Ok(rows);
172        }
173
174        Ok(Vec::new())
175    }
176
177    /// Get a data row relative to the reference datetime
178    pub fn get_row_relative_to_reference(&mut self, relative_index: i32) -> Result<RowValue> {
179        // For now, this is equivalent to get_row since we don't have full DuckDB integration here
180        self.get_row(relative_index)
181    }
182
183    /// Get data rows in a range relative to the reference datetime
184    pub fn get_row_range_relative_to_reference(
185        &mut self,
186        start: i32,
187        end: i32,
188    ) -> Result<Vec<RowValue>> {
189        self.get_row_range(start, end)
190    }
191
192    /// Get all rows for the current identifier and timeframe
193    pub fn get_all_rows(&mut self) -> Result<Vec<RowValue>> {
194        if let Some(ref cache) = self.data_cache {
195            let id = self.get_current_id()?;
196            let timeframe = self.get_current_timeframe()?;
197
198            let count = cache.row_count(&id, &timeframe);
199            let mut rows = Vec::with_capacity(count);
200            for i in 0..count {
201                if let Some(row) = cache.get_row(&id, &timeframe, i) {
202                    rows.push(row);
203                }
204            }
205            return Ok(rows);
206        }
207
208        Ok(Vec::new())
209    }
210}