oracle_rs/
implicit.rs

1//! Implicit results support for Oracle PL/SQL procedures
2//!
3//! Oracle 12.1+ supports returning multiple result sets from PL/SQL procedures
4//! using `dbms_sql.return_result()`. This module provides types to handle these
5//! implicit result sets.
6//!
7//! # Example
8//!
9//! ```rust,ignore
10//! use oracle_rs::Connection;
11//!
12//! let conn = Connection::connect("localhost:1521/ORCLPDB1", "user", "pass").await?;
13//!
14//! // Execute PL/SQL that returns implicit results
15//! let result = conn.execute_plsql(r#"
16//!     declare
17//!         c1 sys_refcursor;
18//!         c2 sys_refcursor;
19//!     begin
20//!         open c1 for select * from employees;
21//!         dbms_sql.return_result(c1);
22//!
23//!         open c2 for select * from departments;
24//!         dbms_sql.return_result(c2);
25//!     end;
26//! "#).await?;
27//!
28//! // Iterate over implicit result sets
29//! for (i, resultset) in result.implicit_results.iter().enumerate() {
30//!     println!("Result set #{}", i + 1);
31//!     for row in &resultset.rows {
32//!         println!("{:?}", row);
33//!     }
34//! }
35//! ```
36
37use crate::row::Row;
38use crate::statement::ColumnInfo;
39
40/// An implicit result set returned from a PL/SQL procedure
41#[derive(Debug, Clone)]
42pub struct ImplicitResult {
43    /// Cursor ID for this result set
44    pub cursor_id: u16,
45    /// Column metadata
46    pub columns: Vec<ColumnInfo>,
47    /// Rows from this result set
48    pub rows: Vec<Row>,
49    /// Whether there are more rows to fetch
50    pub has_more_rows: bool,
51}
52
53impl ImplicitResult {
54    /// Create a new implicit result
55    pub fn new(cursor_id: u16, columns: Vec<ColumnInfo>, rows: Vec<Row>) -> Self {
56        Self {
57            cursor_id,
58            columns,
59            rows,
60            has_more_rows: false,
61        }
62    }
63
64    /// Create an empty implicit result
65    pub fn empty() -> Self {
66        Self {
67            cursor_id: 0,
68            columns: Vec::new(),
69            rows: Vec::new(),
70            has_more_rows: false,
71        }
72    }
73
74    /// Get the number of columns
75    pub fn column_count(&self) -> usize {
76        self.columns.len()
77    }
78
79    /// Get the number of rows
80    pub fn row_count(&self) -> usize {
81        self.rows.len()
82    }
83
84    /// Check if this result set is empty
85    pub fn is_empty(&self) -> bool {
86        self.rows.is_empty()
87    }
88
89    /// Get a column by name
90    pub fn column(&self, name: &str) -> Option<&ColumnInfo> {
91        self.columns.iter().find(|c| c.name.eq_ignore_ascii_case(name))
92    }
93
94    /// Get an iterator over the rows
95    pub fn iter(&self) -> impl Iterator<Item = &Row> {
96        self.rows.iter()
97    }
98}
99
100impl IntoIterator for ImplicitResult {
101    type Item = Row;
102    type IntoIter = std::vec::IntoIter<Row>;
103
104    fn into_iter(self) -> Self::IntoIter {
105        self.rows.into_iter()
106    }
107}
108
109/// Collection of implicit results from a PL/SQL execution
110#[derive(Debug, Clone, Default)]
111pub struct ImplicitResults {
112    /// The implicit result sets
113    pub results: Vec<ImplicitResult>,
114}
115
116impl ImplicitResults {
117    /// Create a new empty collection
118    pub fn new() -> Self {
119        Self { results: Vec::new() }
120    }
121
122    /// Add an implicit result set
123    pub fn add(&mut self, result: ImplicitResult) {
124        self.results.push(result);
125    }
126
127    /// Get the number of result sets
128    pub fn len(&self) -> usize {
129        self.results.len()
130    }
131
132    /// Check if there are no result sets
133    pub fn is_empty(&self) -> bool {
134        self.results.is_empty()
135    }
136
137    /// Get a result set by index
138    pub fn get(&self, index: usize) -> Option<&ImplicitResult> {
139        self.results.get(index)
140    }
141
142    /// Get an iterator over the result sets
143    pub fn iter(&self) -> impl Iterator<Item = &ImplicitResult> {
144        self.results.iter()
145    }
146}
147
148impl IntoIterator for ImplicitResults {
149    type Item = ImplicitResult;
150    type IntoIter = std::vec::IntoIter<ImplicitResult>;
151
152    fn into_iter(self) -> Self::IntoIter {
153        self.results.into_iter()
154    }
155}
156
157#[cfg(test)]
158mod tests {
159    use super::*;
160    use crate::constants::OracleType;
161    use crate::row::Value;
162
163    #[test]
164    fn test_implicit_result_creation() {
165        let columns = vec![
166            ColumnInfo::new("ID", OracleType::Number),
167            ColumnInfo::new("NAME", OracleType::Varchar),
168        ];
169        let rows = vec![
170            Row::new(vec![Value::Integer(1), Value::String("Alice".to_string())]),
171        ];
172        let result = ImplicitResult::new(1, columns, rows);
173
174        assert_eq!(result.cursor_id, 1);
175        assert_eq!(result.column_count(), 2);
176        assert_eq!(result.row_count(), 1);
177        assert!(!result.is_empty());
178    }
179
180    #[test]
181    fn test_implicit_result_empty() {
182        let result = ImplicitResult::empty();
183        assert_eq!(result.cursor_id, 0);
184        assert!(result.is_empty());
185    }
186
187    #[test]
188    fn test_implicit_result_column_lookup() {
189        let columns = vec![
190            ColumnInfo::new("ID", OracleType::Number),
191            ColumnInfo::new("NAME", OracleType::Varchar),
192        ];
193        let result = ImplicitResult::new(1, columns, Vec::new());
194
195        assert!(result.column("ID").is_some());
196        assert!(result.column("id").is_some()); // case-insensitive
197        assert!(result.column("MISSING").is_none());
198    }
199
200    #[test]
201    fn test_implicit_results_collection() {
202        let mut results = ImplicitResults::new();
203        assert!(results.is_empty());
204
205        results.add(ImplicitResult::empty());
206        results.add(ImplicitResult::empty());
207
208        assert_eq!(results.len(), 2);
209        assert!(!results.is_empty());
210        assert!(results.get(0).is_some());
211        assert!(results.get(5).is_none());
212    }
213
214    #[test]
215    fn test_implicit_result_iterator() {
216        let rows = vec![
217            Row::new(vec![Value::Integer(1)]),
218            Row::new(vec![Value::Integer(2)]),
219        ];
220        let result = ImplicitResult::new(1, Vec::new(), rows);
221
222        let collected: Vec<_> = result.iter().collect();
223        assert_eq!(collected.len(), 2);
224    }
225
226    #[test]
227    fn test_implicit_results_iterator() {
228        let mut results = ImplicitResults::new();
229        results.add(ImplicitResult::new(1, Vec::new(), Vec::new()));
230        results.add(ImplicitResult::new(2, Vec::new(), Vec::new()));
231
232        let ids: Vec<_> = results.iter().map(|r| r.cursor_id).collect();
233        assert_eq!(ids, vec![1, 2]);
234    }
235}