oracle_rs/
drcp.rs

1//! Database Resident Connection Pooling (DRCP) support
2//!
3//! DRCP is Oracle's server-side connection pooling feature that allows multiple
4//! client connections to share a smaller pool of server processes, reducing
5//! resource usage on the database server.
6//!
7//! # Example
8//!
9//! ```rust,ignore
10//! use oracle_rs::{Connection, Config, DrcpOptions};
11//!
12//! // Configure DRCP
13//! let drcp = DrcpOptions::new()
14//!     .with_connection_class("MyApp")
15//!     .with_purity(SessionPurity::New);
16//!
17//! // Connect with DRCP enabled
18//! let config = Config::new("host", 1521, "service", "user", "pass")
19//!     .with_drcp(drcp);
20//!
21//! let conn = Connection::connect_with_config(config).await?;
22//!
23//! // Connection will automatically release to pool on close
24//! conn.close().await?;
25//! ```
26
27/// Session purity for DRCP connections
28///
29/// Controls whether to get a fresh session or allow reusing an existing one.
30#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
31pub enum SessionPurity {
32    /// Allow reusing an existing session (default)
33    #[default]
34    Default,
35    /// Request a brand new session
36    New,
37    /// Request an existing session (fail if none available)
38    Self_,
39}
40
41/// Session release mode for DRCP
42#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
43#[repr(u32)]
44pub enum ReleaseMode {
45    /// Normal release back to pool
46    #[default]
47    Normal = 0,
48    /// Release with deauthentication
49    Deauthenticate = 0x00000002,
50}
51
52/// DRCP configuration options
53#[derive(Debug, Clone, Default)]
54pub struct DrcpOptions {
55    /// Connection class for session affinity
56    pub connection_class: Option<String>,
57    /// Session purity requirement
58    pub purity: SessionPurity,
59    /// Whether DRCP is enabled
60    pub enabled: bool,
61}
62
63impl DrcpOptions {
64    /// Create new DRCP options
65    pub fn new() -> Self {
66        Self {
67            connection_class: None,
68            purity: SessionPurity::Default,
69            enabled: true,
70        }
71    }
72
73    /// Set the connection class for session affinity
74    ///
75    /// Sessions with the same connection class may be reused, allowing
76    /// for caching of session state (PL/SQL package variables, etc.)
77    pub fn with_connection_class(mut self, class: impl Into<String>) -> Self {
78        self.connection_class = Some(class.into());
79        self
80    }
81
82    /// Set the session purity
83    pub fn with_purity(mut self, purity: SessionPurity) -> Self {
84        self.purity = purity;
85        self
86    }
87
88    /// Disable DRCP
89    pub fn disabled(mut self) -> Self {
90        self.enabled = false;
91        self
92    }
93
94    /// Check if DRCP is enabled
95    pub fn is_enabled(&self) -> bool {
96        self.enabled
97    }
98}
99
100/// DRCP session state
101#[derive(Debug, Clone, Default)]
102pub struct DrcpSession {
103    /// Whether a DRCP session is held
104    pub is_held: bool,
105    /// Session tag (for session affinity)
106    pub tag: Option<String>,
107    /// Whether the session state has changed
108    pub state_changed: bool,
109}
110
111impl DrcpSession {
112    /// Create a new DRCP session state
113    pub fn new() -> Self {
114        Self::default()
115    }
116
117    /// Mark that a DRCP session is held
118    pub fn set_held(&mut self, held: bool) {
119        self.is_held = held;
120    }
121
122    /// Set the session tag
123    pub fn set_tag(&mut self, tag: Option<String>) {
124        self.tag = tag;
125    }
126
127    /// Check if session state has changed
128    pub fn is_state_changed(&self) -> bool {
129        self.state_changed
130    }
131
132    /// Mark session state as changed
133    pub fn mark_state_changed(&mut self) {
134        self.state_changed = true;
135    }
136
137    /// Clear the state changed flag
138    pub fn clear_state_changed(&mut self) {
139        self.state_changed = false;
140    }
141}
142
143#[cfg(test)]
144mod tests {
145    use super::*;
146
147    #[test]
148    fn test_drcp_options_default() {
149        let opts = DrcpOptions::new();
150        assert!(opts.enabled);
151        assert!(opts.connection_class.is_none());
152        assert_eq!(opts.purity, SessionPurity::Default);
153    }
154
155    #[test]
156    fn test_drcp_options_builder() {
157        let opts = DrcpOptions::new()
158            .with_connection_class("MyApp")
159            .with_purity(SessionPurity::New);
160
161        assert!(opts.is_enabled());
162        assert_eq!(opts.connection_class, Some("MyApp".to_string()));
163        assert_eq!(opts.purity, SessionPurity::New);
164    }
165
166    #[test]
167    fn test_drcp_options_disabled() {
168        let opts = DrcpOptions::new().disabled();
169        assert!(!opts.is_enabled());
170    }
171
172    #[test]
173    fn test_drcp_session_state() {
174        let mut session = DrcpSession::new();
175        assert!(!session.is_held);
176
177        session.set_held(true);
178        assert!(session.is_held);
179
180        session.set_tag(Some("tag1".to_string()));
181        assert_eq!(session.tag, Some("tag1".to_string()));
182    }
183
184    #[test]
185    fn test_drcp_session_state_changed() {
186        let mut session = DrcpSession::new();
187        assert!(!session.is_state_changed());
188
189        session.mark_state_changed();
190        assert!(session.is_state_changed());
191
192        session.clear_state_changed();
193        assert!(!session.is_state_changed());
194    }
195
196    #[test]
197    fn test_session_purity_values() {
198        assert_eq!(SessionPurity::Default, SessionPurity::default());
199    }
200
201    #[test]
202    fn test_release_mode_values() {
203        assert_eq!(ReleaseMode::Normal as u32, 0);
204        assert_eq!(ReleaseMode::Deauthenticate as u32, 0x00000002);
205    }
206}