sibyl/pool/session/nonblocking.rs
1//! Session pool nonblocking mode implementation
2
3use super::{SessionPool, SPool};
4use crate::{Session, Result, oci::{self, *}, Environment, task};
5use std::{ptr, slice, str, marker::PhantomData, sync::Arc};
6
7impl SPool {
8 pub(crate) async fn new(env: &Environment, dblink: &str, username: &str, password: &str, min: usize, inc: usize, max: usize, session_state_fixup_callback: &str) -> Result<Self> {
9 let err = Handle::<OCIError>::new(&env)?;
10 let pool = Handle::<OCISPool>::new(&env)?;
11 let info = Handle::<OCIAuthInfo>::new(&env)?;
12 info.set_attr(OCI_ATTR_DRIVER_NAME, "sibyl", &err)?;
13 if session_state_fixup_callback.len() > 0 {
14 info.set_attr(OCI_ATTR_FIXUP_CALLBACK, session_state_fixup_callback, &err)?;
15 }
16 pool.set_attr(OCI_ATTR_SPOOL_AUTH, info.get_ptr(), &err)?;
17
18 let mut spool = Self { pool, info, err, env: env.get_env(), name: Vec::new() };
19 let dblink = String::from(dblink);
20 let username = String::from(username);
21 let password = String::from(password);
22
23 task::execute_blocking(move || -> Result<Self> {
24 let mut pool_name_ptr = ptr::null::<u8>();
25 let mut pool_name_len = 0u32;
26 oci::session_pool_create(
27 spool.env.as_ref(), spool.err.as_ref(), spool.pool.as_ref(),
28 &mut pool_name_ptr, &mut pool_name_len,
29 dblink.as_ptr(), dblink.len() as _,
30 min as _, max as _, inc as _,
31 username.as_ptr(), username.len() as _,
32 password.as_ptr(), password.len() as _,
33 OCI_SPC_HOMOGENEOUS | OCI_SPC_STMTCACHE
34 )?;
35 let name = unsafe {
36 slice::from_raw_parts(pool_name_ptr, pool_name_len as usize)
37 };
38 spool.name.extend_from_slice(name);
39 Ok(spool)
40 }).await?
41 }
42}
43
44impl<'a> SessionPool<'a> {
45 pub(crate) async fn new(env: &'a Environment, dblink: &str, username: &str, password: &str, min: usize, inc: usize, max: usize, session_state_fixup_callback: &str) -> Result<SessionPool<'a>> {
46 let inner = SPool::new(env, dblink, username, password, min, inc, max, session_state_fixup_callback).await?;
47 let inner = Arc::new(inner);
48 Ok(Self { inner, phantom_env: PhantomData })
49 }
50
51 /**
52 Returns a default (untagged) session from this session pool.
53
54 # Example
55
56 ```
57 use sibyl::{Environment, Session, Date, Result};
58 use once_cell::sync::OnceCell;
59 use std::{env, sync::Arc};
60
61 fn main() -> Result<()> {
62 // `sibyl::block_on` is used to run Sibyl's async doc tests
63 // and would not be used when `main` can be declared as async.
64 sibyl::block_on(async {
65 static ORACLE : OnceCell<Environment> = OnceCell::new();
66 let oracle = ORACLE.get_or_try_init(|| {
67 Environment::new()
68 })?;
69
70 let dbname = env::var("DBNAME").expect("database name");
71 let dbuser = env::var("DBUSER").expect("user name");
72 let dbpass = env::var("DBPASS").expect("password");
73
74 let pool = oracle.create_session_pool(&dbname, &dbuser, &dbpass, 0, 1, 4).await?;
75 let pool = Arc::new(pool);
76
77 let mut workers = Vec::with_capacity(10);
78 for _i in 0..workers.capacity() {
79 let pool = pool.clone();
80 // `sibyl::spawn` abstracts `spawn` for Sibyl's use with different async
81 // runtimes. One does not need it when a specific runtime is selected.
82 let handle = sibyl::spawn(async move {
83 let session = pool.get_session().await?;
84
85 select_latest_hire(&session).await
86 });
87 workers.push(handle);
88 }
89 for handle in workers {
90 let worker_result = handle.await;
91 #[cfg(any(feature="tokio", feature="actix"))]
92 let worker_result = worker_result.expect("completed task result");
93
94 let name = worker_result?;
95 assert_eq!(name, "Amit Banda was hired on April 21, 2008");
96 }
97 Ok(())
98 })
99 }
100
101 async fn select_latest_hire(session: &Session<'_>) -> Result<String> {
102 let stmt = session.prepare("
103 SELECT first_name, last_name, hire_date
104 FROM (
105 SELECT first_name, last_name, hire_date
106 , Row_Number() OVER (ORDER BY hire_date DESC, last_name) AS hire_date_rank
107 FROM hr.employees
108 )
109 WHERE hire_date_rank = 1
110 ").await?;
111 if let Some( row ) = stmt.query_single(()).await? {
112 let first_name : Option<&str> = row.get(0)?;
113 let last_name : &str = row.get(1)?;
114 let name = first_name.map_or(last_name.to_string(), |first_name| format!("{} {}", first_name, last_name));
115 let hire_date : Date = row.get(2)?;
116 let hire_date = hire_date.to_string("FMMonth DD, YYYY")?;
117 Ok(format!("{} was hired on {}", name, hire_date))
118 } else {
119 Ok("Not found".to_string())
120 }
121 }
122 ```
123 */
124 pub async fn get_session(&self) -> Result<Session<'_>> {
125 let (session, _found) = Session::from_session_pool(self, "").await?;
126 Ok(session)
127 }
128
129 /**
130 Returns a tagged session, i.e. a session with the specified type/tag.
131
132 The tags provide a way to customize sessions in the pool. A client can get a default or untagged
133 session from a pool, set certain attributes on the session (such as globalization settings) and
134 label it with an appropriate tag.
135
136 This session or a session with the same attributes can then be requested by providing the same
137 tag that was used during session customization.
138
139 If a user asks for a session with tag 'A', and a matching session is not available, an appropriately
140 authenticated untagged session is returned, if such a session is free.
141
142 # Parameters
143
144 - `taginfo` - Either a single or a [Multi-Property Tag](https://docs.oracle.com/en/database/oracle/oracle-database/19/lnoci/session-and-connection-pooling.html#GUID-DFA21225-E83C-4177-A79A-B8BA29DC662C).
145
146 # Returns
147
148 A tuple with the returned session and the boolean flag. The flag indicates whether the type of the returned
149 session is the same as requested.
150
151 # Example
152
153 ```
154 # fn main() -> sibyl::Result<()> {
155 # sibyl::block_on(async {
156 # use once_cell::sync::OnceCell;
157 # static ORACLE: OnceCell<sibyl::Environment> = OnceCell::new();
158 # let oracle = ORACLE.get_or_try_init(|| sibyl::Environment::new())?;
159 # let dbname = std::env::var("DBNAME").expect("database name");
160 # let dbuser = std::env::var("DBUSER").expect("user name");
161 # let dbpass = std::env::var("DBPASS").expect("password");
162 let pool = oracle.create_session_pool(&dbname, &dbuser, &dbpass, 0, 1, 5).await?;
163
164 {
165 let (session,found) = pool.get_tagged_session("CUSTOM").await?;
166 assert!(!found, "a default (not yet tagged) session was returned");
167 if !found {
168 # let stmt = session.prepare("SELECT value FROM nls_session_parameters WHERE parameter=:PARAM_NAME").await?;
169 # let row = stmt.query_single("NLS_DATE_FORMAT").await?.expect("one row");
170 # let fmt: &str = row.get(0)?;
171 # assert_ne!(fmt, "YYYY-MM-DD", "Default NLS_DATE_FORMAT differs from what we want to set");
172 let stmt = session.prepare("ALTER SESSION SET NLS_DATE_FORMAT='YYYY-MM-DD'").await?;
173 stmt.execute(()).await?;
174 session.set_tag("CUSTOM")?;
175 }
176 } // session is released back to the pool here
177
178 {
179 let (session,found) = pool.get_tagged_session("CUSTOM").await?;
180 assert!(found, "the customized session was returned");
181
182 let stmt = session.prepare("SELECT value FROM nls_session_parameters WHERE parameter=:PARAM_NAME").await?;
183 let row = stmt.query_single("NLS_DATE_FORMAT").await?.expect("one row");
184 let fmt: &str = row.get(0)?;
185 assert_eq!(fmt, "YYYY-MM-DD", "Session uses custom NLS_DATE_FORMAT");
186 }
187 # Ok(()) })
188 # }
189 ```
190 */
191 pub async fn get_tagged_session(&self, taginfo: &str) -> Result<(Session<'_>,bool)> {
192 Session::from_session_pool(self, taginfo).await
193 }
194}