oracle/connection.rs
1// Rust-oracle - Rust binding for Oracle database
2//
3// URL: https://github.com/kubo/rust-oracle
4//
5//-----------------------------------------------------------------------------
6// Copyright (c) 2017-2024 Kubo Takehiro <kubo@jiubao.org>. All rights reserved.
7// This program is free software: you can modify it and/or redistribute it
8// under the terms of:
9//
10// (i) the Universal Permissive License v 1.0 or at your option, any
11// later version (http://oss.oracle.com/licenses/upl); and/or
12//
13// (ii) the Apache License v 2.0. (http://www.apache.org/licenses/LICENSE-2.0)
14//-----------------------------------------------------------------------------
15
16use crate::chkerr;
17use crate::conn::{CloseMode, Info, Purity};
18use crate::error::DPI_ERR_NOT_CONNECTED;
19use crate::oci_attr::data_type::{AttrValue, DataType};
20use crate::oci_attr::handle::ConnHandle;
21use crate::oci_attr::handle::Server;
22use crate::oci_attr::mode::Read;
23use crate::oci_attr::mode::{ReadMode, WriteMode};
24use crate::oci_attr::OciAttr;
25#[cfg(doc)]
26use crate::pool::PoolOptions;
27use crate::sql_type::ObjectType;
28use crate::sql_type::ObjectTypeInternal;
29use crate::sql_type::ToSql;
30use crate::to_rust_str;
31use crate::AssertSend;
32use crate::AssertSync;
33#[cfg(doc)]
34use crate::Batch;
35use crate::BatchBuilder;
36use crate::Context;
37use crate::DpiConn;
38use crate::DpiObjectType;
39use crate::Error;
40use crate::OdpiStr;
41use crate::Result;
42use crate::ResultSet;
43use crate::Row;
44use crate::RowValue;
45use crate::Statement;
46use crate::StatementBuilder;
47use crate::Version;
48use odpic_sys::*;
49use std::borrow::ToOwned;
50use std::collections::HashMap;
51use std::fmt;
52use std::mem::MaybeUninit;
53use std::ptr;
54use std::sync::atomic::{AtomicBool, Ordering};
55use std::sync::Arc;
56use std::sync::Mutex;
57use std::time::Duration;
58
59struct ServerStatus;
60const OCI_ATTR_SERVER_STATUS: u32 = 143;
61const OCI_SERVER_NOT_CONNECTED: u32 = 0;
62const OCI_SERVER_NORMAL: u32 = 1;
63unsafe impl OciAttr for ServerStatus {
64 type HandleType = Server;
65 type Mode = Read;
66 type DataType = u32;
67 const ATTR_NUM: u32 = OCI_ATTR_SERVER_STATUS;
68}
69
70/// Database startup mode
71///
72/// See [`Connection::startup_database`]
73#[derive(Debug, Copy, Clone, PartialEq, Eq)]
74pub enum StartupMode {
75 /// Shuts down a running instance (if there is any) using ABORT before
76 /// starting a new one. This mode should be used only in unusual circumstances.
77 Force,
78
79 /// Allows database access only to users with both the CREATE SESSION
80 /// and RESTRICTED SESSION privileges (normally, the DBA).
81 Restrict,
82}
83
84/// Database shutdown mode
85///
86/// See [`Connection::shutdown_database`].
87#[derive(Debug, Copy, Clone, PartialEq, Eq)]
88pub enum ShutdownMode {
89 /// Further connects are prohibited. Waits for users to disconnect from
90 /// the database.
91 Default,
92
93 /// Further connects are prohibited and no new transactions are allowed.
94 /// Waits for active transactions to complete.
95 Transactional,
96
97 /// Further connects are prohibited and no new transactions are allowed.
98 /// Waits only for local transactions to complete.
99 TransactionalLocal,
100
101 /// Does not wait for current calls to complete or users to disconnect
102 /// from the database. All uncommitted transactions are terminated and
103 /// rolled back.
104 Immediate,
105
106 /// Does not wait for current calls to complete or users to disconnect
107 /// from the database. All uncommitted transactions are terminated and
108 /// are not rolled back. This is the fastest possible way to shut down
109 /// the database, but the next database startup may require instance
110 /// recovery. Therefore, this option should be used only in unusual
111 /// circumstances; for example, if a background process terminates abnormally.
112 Abort,
113
114 /// Shuts down the database. Should be used only in the second call
115 /// to [`Connection::shutdown_database`] after the database is closed and dismounted.
116 Final,
117}
118
119/// [Administrative privilege](https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-633842B8-4B19-4F96-A757-783BF62825A7)
120///
121/// See [`Connector::privilege`].
122#[derive(Debug, Copy, Clone, PartialEq, Eq)]
123pub enum Privilege {
124 /// Connects as [SYSDBA](https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-BD5D39D1-DBFF-400A-8645-355F8FB9CD31).
125 ///
126 Sysdba,
127
128 /// Connects as [SYSOPER](https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-BD5D39D1-DBFF-400A-8645-355F8FB9CD31).
129 Sysoper,
130
131 /// Connects as [SYSASM](https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-7396FD18-628B-4026-AA55-79C6D6205EAE) (Oracle 12c or later)
132 Sysasm,
133
134 /// Connects as [SYSBACKUP](https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-BF12E37F-4606-42BB-B8B6-4CDC5A870EE7)
135 Sysbackup,
136
137 /// Connects as [SYSDG](https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-5798F976-85B2-4973-92F7-DB3F6BC9D497) (Oracle 12c or later)
138 Sysdg,
139
140 /// Connects as [SYSKM](https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-573B5831-E106-4D8C-9101-CF9C1B74A39C) (Oracle 12c or later)
141 Syskm,
142
143 /// Connects as [SYSRAC](https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-69D0614C-D24E-4EC1-958A-79D7CCA3FA3A) (Oracle 12c R2 or later)
144 Sysrac,
145}
146
147impl Privilege {
148 pub(crate) fn to_dpi(self) -> dpiAuthMode {
149 match self {
150 Privilege::Sysdba => DPI_MODE_AUTH_SYSDBA,
151 Privilege::Sysoper => DPI_MODE_AUTH_SYSOPER,
152 Privilege::Sysasm => DPI_MODE_AUTH_SYSASM,
153 Privilege::Sysbackup => DPI_MODE_AUTH_SYSBKP,
154 Privilege::Sysdg => DPI_MODE_AUTH_SYSDGD,
155 Privilege::Syskm => DPI_MODE_AUTH_SYSKMT,
156 Privilege::Sysrac => DPI_MODE_AUTH_SYSRAC,
157 }
158 }
159}
160
161#[derive(Debug, Copy, Clone, PartialEq, Eq)]
162/// Connection status
163pub enum ConnStatus {
164 /// The connection is alive. See [`Connection::status`] for details.
165 Normal,
166 /// The connection has been terminated. See [`Connection::status`] for details.
167 NotConnected,
168 /// The connection has been closed by [`Connection::close`].
169 Closed,
170}
171
172#[derive(Debug, Default, Clone, PartialEq)]
173pub(crate) struct CommonCreateParamsBuilder {
174 events: bool,
175 edition: Option<String>,
176 driver_name: Option<String>,
177 stmt_cache_size: Option<u32>,
178}
179
180impl CommonCreateParamsBuilder {
181 pub fn events(&mut self, b: bool) {
182 self.events = b;
183 }
184
185 pub fn edition<S>(&mut self, edition: S)
186 where
187 S: Into<String>,
188 {
189 self.edition = Some(edition.into());
190 }
191
192 pub fn driver_name<S>(&mut self, driver_name: S)
193 where
194 S: Into<String>,
195 {
196 self.driver_name = Some(driver_name.into());
197 }
198
199 pub fn stmt_cache_size(&mut self, size: u32) {
200 self.stmt_cache_size = Some(size);
201 }
202
203 pub fn build(&self, ctxt: &Context) -> dpiCommonCreateParams {
204 let mut common_params = ctxt.common_create_params();
205 if self.events {
206 common_params.createMode |= DPI_MODE_CREATE_EVENTS;
207 }
208 if let Some(ref s) = self.edition {
209 let s = OdpiStr::new(s);
210 common_params.edition = s.ptr;
211 common_params.editionLength = s.len;
212 }
213 if let Some(ref s) = self.driver_name {
214 let s = OdpiStr::new(s);
215 common_params.driverName = s.ptr;
216 common_params.driverNameLength = s.len;
217 }
218 if let Some(s) = self.stmt_cache_size {
219 common_params.stmtCacheSize = s;
220 }
221 common_params
222 }
223}
224
225/// Builder data type to create Connection.
226///
227/// When a connection can be established only with username, password
228/// and connect string, use [`Connection::connect`] instead.
229#[derive(Debug, Clone, PartialEq)]
230pub struct Connector {
231 username: String,
232 password: String,
233 connect_string: String,
234 privilege: Option<Privilege>,
235 external_auth: bool,
236 prelim_auth: bool,
237 new_password: String,
238 purity: Option<Purity>,
239 connection_class: String,
240 app_context: Vec<(String, String, String)>,
241 common_params: CommonCreateParamsBuilder,
242}
243
244impl Connector {
245 /// Create a connector
246 pub fn new<U, P, C>(username: U, password: P, connect_string: C) -> Connector
247 where
248 U: Into<String>,
249 P: Into<String>,
250 C: Into<String>,
251 {
252 Connector {
253 username: username.into(),
254 password: password.into(),
255 connect_string: connect_string.into(),
256 privilege: None,
257 external_auth: false,
258 prelim_auth: false,
259 new_password: "".into(),
260 purity: None,
261 connection_class: "".into(),
262 app_context: vec![],
263 common_params: Default::default(),
264 }
265 }
266
267 /// Set [administrative privilege](https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-633842B8-4B19-4F96-A757-783BF62825A7).
268 ///
269 /// # Examples
270 ///
271 /// ```no_run
272 /// # use oracle::*;
273 /// // connect system/manager as sysdba
274 /// let conn = Connector::new("system", "manager", "")
275 /// .privilege(Privilege::Sysdba)
276 /// .connect()?;
277 /// # Ok::<(), Error>(())
278 /// ```
279 pub fn privilege(&mut self, privilege: Privilege) -> &mut Connector {
280 self.privilege = Some(privilege);
281 self
282 }
283
284 /// Uses external authentication such as [OS authentication][].
285 ///
286 /// # Examples
287 ///
288 /// ```no_run
289 /// # use oracle::*;
290 /// let conn = Connector::new("", "", "")
291 /// .external_auth(true)
292 /// .connect()?;
293 /// # Ok::<(), Error>(())
294 /// ```
295 ///
296 /// [OS authentication]: https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-37BECE32-58D5-43BF-A098-97936D66968F
297 pub fn external_auth(&mut self, b: bool) -> &mut Connector {
298 self.external_auth = b;
299 self
300 }
301
302 /// Sets prelim auth mode to connect to an idle instance.
303 ///
304 /// See [starting up a database](Connection::startup_database).
305 pub fn prelim_auth(&mut self, b: bool) -> &mut Connector {
306 self.prelim_auth = b;
307 self
308 }
309
310 /// Sets new password during establishing a connection.
311 ///
312 /// When a password is expired, you cannot connect to the user.
313 /// A new password must be set by other user or set during establishing
314 /// a connection.
315 ///
316 /// # Examples
317 ///
318 /// Connect to user `scott` with password `tiger`. If the password
319 /// is expired, set a new password `jaguar`.
320 ///
321 /// ```no_run
322 /// # use oracle::*;
323 /// let conn = match Connection::connect("scott", "tiger", "") {
324 /// Ok(conn) => conn,
325 /// Err(Error::OciError(dberr)) if dberr.code() == 28001 => {
326 /// // ORA-28001: the password has expired
327 /// Connector::new("scott", "tiger", "")
328 /// .new_password("jaguar")
329 /// .connect()?
330 /// }
331 /// Err(err) => return Err(err),
332 /// };
333 /// # Ok::<(), Error>(())
334 /// ```
335 pub fn new_password<P>(&mut self, password: P) -> &mut Connector
336 where
337 P: Into<String>,
338 {
339 self.new_password = password.into();
340 self
341 }
342
343 /// Sets session purity specifying whether an application can reuse a pooled session (`Purity::Self_`) or must use a new session (`Purity::New`) from [DRCP][] pooled sessions.
344 ///
345 /// [DRCP]: https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-015CA8C1-2386-4626-855D-CC546DDC1086
346 pub fn purity(&mut self, purity: Purity) -> &mut Connector {
347 self.purity = Some(purity);
348 self
349 }
350
351 /// Sets a connection class to restrict sharing [DRCP][] pooled sessions.
352 ///
353 /// See [here][] for more detail.
354 ///
355 /// [DRCP]: https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-015CA8C1-2386-4626-855D-CC546DDC1086
356 /// [here]: https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-EC3DEE61-512C-4CBB-A431-91894D0E1E37
357 pub fn connection_class<S>(&mut self, connection_class: S) -> &mut Connector
358 where
359 S: Into<String>,
360 {
361 self.connection_class = connection_class.into();
362 self
363 }
364
365 /// Appends an application context.
366 ///
367 /// See [Oracle manual](https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-5841261E-988F-4A56-A2B4-71114AB3D51D)
368 ///
369 /// This is same with [DBMS_SESSION.SET_CONTEXT][] but this can set application contexts before a connection is established.
370 ///
371 /// [DBMS_SESSION.SET_CONTEXT]: https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-395C622C-ED79-44CC-9157-6A320934F2A9
372 ///
373 /// # Examples
374 ///
375 /// ```
376 /// # use oracle::{Connector, Error};
377 /// # use oracle::test_util;
378 /// # let username = test_util::main_user();
379 /// # let password = test_util::main_password();
380 /// # let connect_string = test_util::connect_string();
381 /// let conn = Connector::new(username, password, connect_string)
382 /// .app_context("CLIENTCONTEXT", "foo", "bar")
383 /// .app_context("CLIENTCONTEXT", "baz", "qux")
384 /// .connect()?;
385 /// let val = conn.query_row_as::<String>("select sys_context('CLIENTCONTEXT', 'foo') from dual", &[])?;
386 /// assert_eq!(val, "bar");
387 /// let val = conn.query_row_as::<String>("select sys_context('CLIENTCONTEXT', 'baz') from dual", &[])?;
388 /// assert_eq!(val, "qux");
389 /// # Ok::<(), Error>(())
390 /// ```
391 pub fn app_context<T1, T2, T3>(&mut self, namespace: T1, name: T2, value: T3) -> &mut Connector
392 where
393 T1: Into<String>,
394 T2: Into<String>,
395 T3: Into<String>,
396 {
397 self.app_context
398 .push((namespace.into(), name.into(), value.into()));
399 self
400 }
401
402 // Remove later
403 #[doc(hidden)]
404 pub fn tag<S>(&mut self, _tag: S) -> &mut Connector
405 where
406 S: Into<String>,
407 {
408 self
409 }
410
411 // Remove later
412 #[doc(hidden)]
413 pub fn match_any_tag(&mut self, _b: bool) -> &mut Connector {
414 self
415 }
416
417 /// Reserved for when advanced queuing (AQ) or continuous query
418 /// notification (CQN) is supported.
419 pub fn events(&mut self, b: bool) -> &mut Connector {
420 self.common_params.events(b);
421 self
422 }
423
424 /// Specifies edition of [Edition-Based Redefinition][].
425 ///
426 /// [Edition-Based Redefinition]: https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-58DE05A0-5DEF-4791-8FA8-F04D11964906
427 pub fn edition<S>(&mut self, edition: S) -> &mut Connector
428 where
429 S: Into<String>,
430 {
431 self.common_params.edition(edition);
432 self
433 }
434
435 /// Sets the driver name displayed in [V$SESSION_CONNECT_INFO.CLIENT_DRIVER][].
436 ///
437 /// The default value is "rust-oracle : version number". Only the first 8
438 /// chracters "rust-ora" are displayed when the Oracle server version is
439 /// lower than 12.0.1.2.
440 ///
441 /// [V$SESSION_CONNECT_INFO.CLIENT_DRIVER]: https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-9F0DCAEA-A67E-4183-89E7-B1555DC591CE
442 pub fn driver_name<S>(&mut self, driver_name: S) -> &mut Connector
443 where
444 S: Into<String>,
445 {
446 self.common_params.driver_name(driver_name);
447 self
448 }
449
450 /// Specifies the number of statements to retain in the statement cache. Use a
451 /// value of 0 to disable the statement cache completely.
452 ///
453 /// The default value is 20.
454 ///
455 /// See also [`Connection::stmt_cache_size`] and [`Connection::set_stmt_cache_size`]
456 pub fn stmt_cache_size(&mut self, size: u32) -> &mut Connector {
457 self.common_params.stmt_cache_size(size);
458 self
459 }
460
461 /// Connect an Oracle server using specified parameters
462 pub fn connect(&self) -> Result<Connection> {
463 let ctxt = Context::new()?;
464 let common_params = self.common_params.build(&ctxt);
465 let (conn_params, _app_contexts) = self.to_dpi_conn_create_params(&ctxt);
466 Connection::connect_internal(
467 ctxt,
468 &self.username,
469 &self.password,
470 &self.connect_string,
471 common_params,
472 conn_params,
473 )
474 }
475
476 fn to_dpi_conn_create_params(
477 &self,
478 ctxt: &Context,
479 ) -> (dpiConnCreateParams, Vec<dpiAppContext>) {
480 let mut conn_params = ctxt.conn_create_params();
481
482 if let Some(ref privilege) = self.privilege {
483 conn_params.authMode |= privilege.to_dpi();
484 }
485 if self.external_auth {
486 conn_params.externalAuth = 1;
487 }
488 if self.prelim_auth {
489 conn_params.authMode |= DPI_MODE_AUTH_PRELIM;
490 }
491 let s = OdpiStr::new(&self.new_password);
492 conn_params.newPassword = s.ptr;
493 conn_params.newPasswordLength = s.len;
494 if let Some(purity) = self.purity {
495 conn_params.purity = purity.to_dpi();
496 }
497 let s = OdpiStr::new(&self.connection_class);
498 conn_params.connectionClass = s.ptr;
499 conn_params.connectionClassLength = s.len;
500 let mut app_context = Vec::with_capacity(self.app_context.len());
501 for ac in &self.app_context {
502 let namespace = OdpiStr::new(&ac.0);
503 let name = OdpiStr::new(&ac.1);
504 let value = OdpiStr::new(&ac.2);
505 app_context.push(dpiAppContext {
506 namespaceName: namespace.ptr,
507 namespaceNameLength: namespace.len,
508 name: name.ptr,
509 nameLength: name.len,
510 value: value.ptr,
511 valueLength: value.len,
512 });
513 }
514 if !app_context.is_empty() {
515 conn_params.appContext = app_context.as_mut_ptr();
516 conn_params.numAppContext = app_context.len() as u32;
517 }
518 (conn_params, app_context)
519 }
520}
521
522pub(crate) type Conn = Arc<InnerConn>;
523
524pub(crate) struct InnerConn {
525 ctxt: Context,
526 pub(crate) handle: DpiConn,
527 pub(crate) autocommit: AtomicBool,
528 pub(crate) objtype_cache: Mutex<HashMap<String, Arc<ObjectTypeInternal>>>,
529 tag: String,
530 tag_found: bool,
531 is_new_connection: bool,
532}
533
534impl InnerConn {
535 pub fn new(
536 ctxt: Context,
537 handle: *mut dpiConn,
538 conn_params: &dpiConnCreateParams,
539 ) -> InnerConn {
540 InnerConn {
541 ctxt,
542 handle: DpiConn::new(handle),
543 autocommit: AtomicBool::new(false),
544 objtype_cache: Mutex::new(HashMap::new()),
545 tag: to_rust_str(conn_params.outTag, conn_params.outTagLength),
546 tag_found: conn_params.outTagFound != 0,
547 is_new_connection: conn_params.outNewSession != 0,
548 }
549 }
550
551 pub(crate) fn ctxt(&self) -> &Context {
552 &self.ctxt
553 }
554
555 pub fn autocommit(&self) -> bool {
556 self.autocommit.load(Ordering::Relaxed)
557 }
558
559 pub fn clear_object_type_cache(&self) -> Result<()> {
560 self.objtype_cache.lock()?.clear();
561 Ok(())
562 }
563}
564
565impl fmt::Debug for InnerConn {
566 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
567 write!(
568 f,
569 "Conn {{ handle: {:?}, autocommit: {:?}",
570 self.handle.raw(),
571 self.autocommit,
572 )?;
573 if !self.tag.is_empty() {
574 write!(f, ", tag: {:?}", self.tag)?;
575 }
576 if self.tag_found {
577 write!(f, ", tag_found: {:?}", self.tag_found)?;
578 }
579 write!(f, " }}")
580 }
581}
582
583/// Connection to an Oracle database
584pub struct Connection {
585 pub(crate) conn: Conn,
586}
587
588impl AssertSync for Connection {}
589impl AssertSend for Connection {}
590
591impl Connection {
592 /// Connects to an Oracle server using username, password and connect string.
593 ///
594 /// If you need to connect the server with additional parameters
595 /// such as SYSDBA privilege, use [`Connector`] instead.
596 ///
597 /// # Examples
598 ///
599 /// Connect to a local database.
600 ///
601 /// ```no_run
602 /// # use oracle::*;
603 /// let conn = Connection::connect("scott", "tiger", "")?;
604 /// # Ok::<(), Error>(())
605 /// ```
606 ///
607 /// Connect to a remote database specified by easy connect naming.
608 ///
609 /// ```no_run
610 /// # use oracle::*;
611 /// let conn = Connection::connect("scott", "tiger",
612 /// "server_name:1521/service_name")?;
613 /// # Ok::<(), Error>(())
614 /// ```
615 pub fn connect<U, P, C>(username: U, password: P, connect_string: C) -> Result<Connection>
616 where
617 U: AsRef<str>,
618 P: AsRef<str>,
619 C: AsRef<str>,
620 {
621 let ctxt = Context::new()?;
622 let common_params = ctxt.common_create_params();
623 let conn_params = ctxt.conn_create_params();
624 Connection::connect_internal(
625 ctxt,
626 username.as_ref(),
627 password.as_ref(),
628 connect_string.as_ref(),
629 common_params,
630 conn_params,
631 )
632 }
633
634 fn connect_internal(
635 ctxt: Context,
636 username: &str,
637 password: &str,
638 connect_string: &str,
639 common_params: dpiCommonCreateParams,
640 mut conn_params: dpiConnCreateParams,
641 ) -> Result<Connection> {
642 let username = OdpiStr::new(username);
643 let password = OdpiStr::new(password);
644 let connect_string = OdpiStr::new(connect_string);
645 let mut handle = ptr::null_mut();
646 chkerr!(
647 &ctxt,
648 dpiConn_create(
649 ctxt.context,
650 username.ptr,
651 username.len,
652 password.ptr,
653 password.len,
654 connect_string.ptr,
655 connect_string.len,
656 &common_params,
657 &mut conn_params,
658 &mut handle
659 )
660 );
661 ctxt.set_warning();
662 conn_params.outNewSession = 1;
663 Ok(Connection::from_dpi_handle(ctxt, handle, &conn_params))
664 }
665
666 pub(crate) fn from_conn(conn: Conn) -> Connection {
667 Connection { conn }
668 }
669
670 pub(crate) fn from_dpi_handle(
671 ctxt: Context,
672 handle: *mut dpiConn,
673 params: &dpiConnCreateParams,
674 ) -> Connection {
675 Connection {
676 conn: Arc::new(InnerConn::new(ctxt, handle, params)),
677 }
678 }
679
680 pub(crate) fn ctxt(&self) -> &Context {
681 &self.conn.ctxt
682 }
683
684 pub(crate) fn handle(&self) -> *mut dpiConn {
685 self.conn.handle.raw()
686 }
687
688 /// Closes the connection before the end of lifetime.
689 ///
690 /// This fails when open statements or LOBs exist.
691 pub fn close(&self) -> Result<()> {
692 self.close_with_mode(CloseMode::Default)
693 }
694
695 pub fn close_with_mode(&self, mode: CloseMode) -> Result<()> {
696 let (mode, tag) = match mode {
697 CloseMode::Default => (DPI_MODE_CONN_CLOSE_DEFAULT, ""),
698 CloseMode::Drop => (DPI_MODE_CONN_CLOSE_DROP, ""),
699 CloseMode::Retag(tag) => (DPI_MODE_CONN_CLOSE_RETAG, tag),
700 };
701 let tag = OdpiStr::new(tag);
702 chkerr!(
703 self.ctxt(),
704 dpiConn_close(self.handle(), mode, tag.ptr, tag.len)
705 );
706 Ok(())
707 }
708
709 /// Creates [`StatementBuilder`][] to create a [`Statement`][]
710 ///
711 /// # Examples
712 ///
713 /// Executes a SQL statement with different parameters.
714 ///
715 /// ```no_run
716 /// # use oracle::*;
717 /// # let conn = Connection::connect("scott", "tiger", "")?;
718 /// let mut stmt = conn.statement("insert into emp(empno, ename) values (:id, :name)").build()?;
719 ///
720 /// let emp_list = [
721 /// (7369, "Smith"),
722 /// (7499, "Allen"),
723 /// (7521, "Ward"),
724 /// ];
725 ///
726 /// // insert rows using positional parameters
727 /// for emp in &emp_list {
728 /// stmt.execute(&[&emp.0, &emp.1])?;
729 /// }
730 ///
731 /// let emp_list = [
732 /// (7566, "Jones"),
733 /// (7654, "Martin"),
734 /// (7698, "Blake"),
735 /// ];
736 ///
737 /// // insert rows using named parameters
738 /// for emp in &emp_list {
739 /// stmt.execute_named(&[("id", &emp.0), ("name", &emp.1)])?;
740 /// }
741 /// # Ok::<(), Error>(())
742 /// ```
743 ///
744 /// Query methods in Connection allocate memory for 100 rows by default
745 /// to reduce the number of network round trips in case that many rows are
746 /// fetched. When 100 isn't preferable, use [`StatementBuilder::fetch_array_size`]
747 /// to customize it.
748 ///
749 /// ```no_run
750 /// # use oracle::*;
751 /// # let conn = Connection::connect("scott", "tiger", "")?;
752 /// // fetch top 10 rows.
753 /// let mut stmt = conn
754 /// .statement("select empno, ename from emp order by empno fetch first 10 rows only")
755 /// .fetch_array_size(10)
756 /// .build()?;
757 /// for row_result in stmt.query_as::<(i32, String)>(&[])? {
758 /// let (empno, ename) = row_result?;
759 /// println!("empno: {}, ename: {}", empno, ename);
760 /// }
761 /// # Ok::<(), Error>(())
762 /// ```
763 ///
764 /// By default, a maximum of 2 rows are returned when the query is first
765 /// executed. To modify this, use [`StatementBuilder::prefetch_rows`] to customize
766 /// it. For more information on the difference between this and `fetch_array_size`,
767 /// see [this writeup](https://blog.dbi-services.com/arraysize-or-rowprefetch-in-sqlplus/)
768 /// or [this description](https://oracle.github.io/node-oracledb/doc/api.html#rowfetching).
769 ///
770 /// ```no_run
771 /// # use oracle::*;
772 /// # let conn = Connection::connect("scott", "tiger", "")?;
773 /// // fetch top 10 rows.
774 /// let mut stmt = conn
775 /// .statement("select empno, ename from emp order by empno fetch first 10 rows only")
776 /// .prefetch_rows(11) // add one to avoid a round-trip to check for end-of-fetch
777 /// .build()?;
778 /// for row_result in stmt.query_as::<(i32, String)>(&[])? {
779 /// let (empno, ename) = row_result?;
780 /// println!("empno: {}, ename: {}", empno, ename);
781 /// }
782 /// # Ok::<(), Error>(())
783 /// ```
784 pub fn statement<'conn, 'sql>(&'conn self, sql: &'sql str) -> StatementBuilder<'conn, 'sql> {
785 StatementBuilder::new(self, sql)
786 }
787
788 /// Creates [BatchBuilder][]
789 ///
790 /// See [`Batch`].
791 pub fn batch<'conn, 'sql>(
792 &'conn self,
793 sql: &'sql str,
794 max_batch_size: usize,
795 ) -> BatchBuilder<'conn, 'sql> {
796 BatchBuilder::new(self, sql, max_batch_size)
797 }
798
799 /// Executes a select statement and returns a result set containing [`Row`]s.
800 ///
801 /// See [Query Methods][].
802 ///
803 /// [Query Methods]: https://github.com/kubo/rust-oracle/blob/master/docs/query-methods.md
804 pub fn query(&self, sql: &str, params: &[&dyn ToSql]) -> Result<ResultSet<'static, Row>> {
805 let mut stmt = self.statement(sql).build()?;
806 stmt.exec(params, true, "query")?;
807 Ok(ResultSet::<Row>::from_stmt(stmt.stmt))
808 }
809
810 /// Executes a select statement using named parameters and returns a result set containing [`Row`]s.
811 ///
812 /// See [Query Methods][].
813 ///
814 /// [Query Methods]: https://github.com/kubo/rust-oracle/blob/master/docs/query-methods.md
815 pub fn query_named(
816 &self,
817 sql: &str,
818 params: &[(&str, &dyn ToSql)],
819 ) -> Result<ResultSet<'static, Row>> {
820 let mut stmt = self.statement(sql).build()?;
821 stmt.exec_named(params, true, "query_named")?;
822 Ok(ResultSet::<Row>::from_stmt(stmt.stmt))
823 }
824
825 /// Executes a select statement and returns a result set containing [`RowValue`]s.
826 ///
827 /// See [Query Methods][].
828 ///
829 /// [Query Methods]: https://github.com/kubo/rust-oracle/blob/master/docs/query-methods.md
830 pub fn query_as<T>(&self, sql: &str, params: &[&dyn ToSql]) -> Result<ResultSet<'static, T>>
831 where
832 T: RowValue,
833 {
834 let mut stmt = self.statement(sql).build()?;
835 stmt.exec(params, true, "query_as")?;
836 Ok(ResultSet::<T>::from_stmt(stmt.stmt))
837 }
838
839 /// Executes a select statement using named parameters and returns a result set containing [`RowValue`]s.
840 ///
841 /// See [Query Methods][].
842 ///
843 /// [Query Methods]: https://github.com/kubo/rust-oracle/blob/master/docs/query-methods.md
844 pub fn query_as_named<T>(
845 &self,
846 sql: &str,
847 params: &[(&str, &dyn ToSql)],
848 ) -> Result<ResultSet<'static, T>>
849 where
850 T: RowValue,
851 {
852 let mut stmt = self.statement(sql).build()?;
853 stmt.exec_named(params, true, "query_as_named")?;
854 Ok(ResultSet::<T>::from_stmt(stmt.stmt))
855 }
856
857 /// Gets one row from a query using positoinal bind parameters.
858 ///
859 /// See [Query Methods][].
860 ///
861 /// [Query Methods]: https://github.com/kubo/rust-oracle/blob/master/docs/query-methods.md
862 pub fn query_row(&self, sql: &str, params: &[&dyn ToSql]) -> Result<Row> {
863 let mut stmt = self.statement(sql).fetch_array_size(1).build()?;
864 stmt.query_row(params)?;
865 Ok(stmt.stmt.row.take().unwrap())
866 }
867
868 /// Gets one row from a query using named bind parameters.
869 ///
870 /// See [Query Methods][].
871 ///
872 /// [Query Methods]: https://github.com/kubo/rust-oracle/blob/master/docs/query-methods.md
873 pub fn query_row_named(&self, sql: &str, params: &[(&str, &dyn ToSql)]) -> Result<Row> {
874 let mut stmt = self.statement(sql).fetch_array_size(1).build()?;
875 stmt.query_row_named(params)?;
876 Ok(stmt.stmt.row.take().unwrap())
877 }
878
879 /// Gets one row from a query as specified type.
880 ///
881 /// See [Query Methods][].
882 ///
883 /// [Query Methods]: https://github.com/kubo/rust-oracle/blob/master/docs/query-methods.md
884 pub fn query_row_as<T>(&self, sql: &str, params: &[&dyn ToSql]) -> Result<T>
885 where
886 T: RowValue,
887 {
888 let mut stmt = self.statement(sql).fetch_array_size(1).build()?;
889 stmt.query_row_as::<T>(params)
890 }
891
892 /// Gets one row from a query with named bind parameters as specified type.
893 ///
894 /// See [Query Methods][].
895 ///
896 /// [Query Methods]: https://github.com/kubo/rust-oracle/blob/master/docs/query-methods.md
897 pub fn query_row_as_named<T>(&self, sql: &str, params: &[(&str, &dyn ToSql)]) -> Result<T>
898 where
899 T: RowValue,
900 {
901 let mut stmt = self.statement(sql).fetch_array_size(1).build()?;
902 stmt.query_row_as_named::<T>(params)
903 }
904
905 /// Creates a statement, binds values by position and executes it in one call.
906 /// It will retunrs `Err` when the statemnet is a select statement.
907 ///
908 /// # Examples
909 ///
910 /// ```no_run
911 /// # use oracle::*;
912 /// let conn = Connection::connect("scott", "tiger", "")?;
913 ///
914 /// // execute a statement without bind parameters
915 /// conn.execute("insert into emp(empno, ename) values (113, 'John')", &[])?;
916 ///
917 /// // execute a statement with binding parameters by position
918 /// conn.execute("insert into emp(empno, ename) values (:1, :2)", &[&114, &"Smith"])?;
919 ///
920 /// # Ok::<(), Error>(())
921 /// ```
922 pub fn execute(&self, sql: &str, params: &[&dyn ToSql]) -> Result<Statement> {
923 let mut stmt = self.statement(sql).build()?;
924 stmt.execute(params)?;
925 Ok(stmt)
926 }
927
928 /// Creates a statement, binds values by name and executes it in one call.
929 /// It will retunrs `Err` when the statemnet is a select statement.
930 ///
931 /// The bind variable names are compared case-insensitively.
932 ///
933 /// # Examples
934 ///
935 /// ```no_run
936 /// # use oracle::*;
937 /// let conn = Connection::connect("scott", "tiger", "")?;
938 ///
939 /// // execute a statement with binding parameters by name
940 /// conn.execute_named("insert into emp(empno, ename) values (:id, :name)",
941 /// &[("id", &114),
942 /// ("name", &"Smith")])?;
943 ///
944 /// # Ok::<(), Error>(())
945 /// ```
946 pub fn execute_named(&self, sql: &str, params: &[(&str, &dyn ToSql)]) -> Result<Statement> {
947 let mut stmt = self.statement(sql).build()?;
948 stmt.execute_named(params)?;
949 Ok(stmt)
950 }
951
952 /// Commits the current active transaction
953 pub fn commit(&self) -> Result<()> {
954 chkerr!(self.ctxt(), dpiConn_commit(self.handle()));
955 Ok(())
956 }
957
958 /// Rolls back the current active transaction
959 pub fn rollback(&self) -> Result<()> {
960 chkerr!(self.ctxt(), dpiConn_rollback(self.handle()));
961 Ok(())
962 }
963
964 /// Gets autocommit mode.
965 /// It is false by default.
966 pub fn autocommit(&self) -> bool {
967 self.conn.autocommit()
968 }
969
970 /// Enables or disables autocommit mode.
971 /// It is disabled by default.
972 pub fn set_autocommit(&mut self, autocommit: bool) {
973 self.conn.autocommit.store(autocommit, Ordering::Relaxed)
974 }
975
976 /// Cancels execution of running statements in the connection
977 ///
978 /// # Examples
979 ///
980 /// ```
981 /// # use oracle::Error;
982 /// # use oracle::test_util;
983 /// # use std::sync::Arc;
984 /// # use std::thread::{self, sleep};
985 /// # use std::time::{Duration, Instant};
986 /// # let conn = test_util::connect()?;
987 ///
988 /// // Wrap conn with Arc to be share it with threads.
989 /// let conn = Arc::new(conn);
990 ///
991 /// let now = Instant::now();
992 /// let range = Duration::from_secs(3)..=Duration::from_secs(20);
993 ///
994 /// // Start a thread to cancel a query
995 /// let cloned_conn = conn.clone();
996 /// let join_handle = thread::spawn(move || {
997 /// sleep(Duration::from_secs(3));
998 /// cloned_conn.break_execution()
999 /// });
1000 ///
1001 /// // This query is canceled by break_execution.
1002 /// let result = conn.query_row_as::<u64>("select count(*) from all_objects, all_objects, all_objects, all_objects, all_objects", &[]);
1003 /// assert!(result.is_err());
1004 /// let elapsed = now.elapsed();
1005 /// assert!(range.contains(&elapsed), "cancel: {:?}, {:?}", elapsed, result.unwrap_err());
1006 /// # join_handle.join().unwrap();
1007 /// # Ok::<(), Error>(())
1008 /// ```
1009 pub fn break_execution(&self) -> Result<()> {
1010 chkerr!(self.ctxt(), dpiConn_breakExecution(self.handle()));
1011 Ok(())
1012 }
1013
1014 /// Gets an object type information from name
1015 ///
1016 /// ```
1017 /// # use oracle::Error;
1018 /// # use oracle::test_util;
1019 /// # let conn = test_util::connect()?;
1020 /// let objtype = conn.object_type("SDO_GEOMETRY")?;
1021 /// assert_eq!(objtype.schema(), "MDSYS");
1022 /// assert_eq!(objtype.name(), "SDO_GEOMETRY");
1023 /// # Ok::<(), Error>(())
1024 /// ```
1025 ///
1026 /// Note that the object type is cached in the connection.
1027 /// However when "CREATE TYPE", "ALTER TYPE" or "DROP TYPE"
1028 /// is executed, the cache clears.
1029 pub fn object_type(&self, name: &str) -> Result<ObjectType> {
1030 {
1031 let guard = self.conn.objtype_cache.lock()?;
1032 if let Some(rc_objtype) = guard.get(name) {
1033 return Ok(ObjectType {
1034 internal: rc_objtype.clone(),
1035 });
1036 }
1037 }
1038 let s = OdpiStr::new(name);
1039 let mut handle = ptr::null_mut();
1040 chkerr!(
1041 self.ctxt(),
1042 dpiConn_getObjectType(self.handle(), s.ptr, s.len, &mut handle)
1043 );
1044 let res = ObjectType::from_dpi_object_type(self.conn.clone(), DpiObjectType::new(handle));
1045 if let Ok(ref objtype) = res {
1046 self.conn
1047 .objtype_cache
1048 .lock()?
1049 .insert(name.to_string(), objtype.internal.clone());
1050 };
1051 res
1052 }
1053
1054 /// Clear the object type cache in the connection.
1055 ///
1056 /// See also [`object_type`](#method.object_type).
1057 pub fn clear_object_type_cache(&self) -> Result<()> {
1058 self.conn.clear_object_type_cache()
1059 }
1060
1061 #[doc(hidden)]
1062 pub fn object_type_cache_len(&self) -> usize {
1063 self.conn.objtype_cache.lock().unwrap().len()
1064 }
1065
1066 /// Gets information about the server version
1067 ///
1068 /// NOTE: if you connect to Oracle Database 18 or higher with
1069 /// Oracle client libraries 12.2 or lower, it gets the base
1070 /// version (such as 18.0.0.0.0) instead of the full version
1071 /// (such as 18.3.0.0.0).
1072 ///
1073 /// # Examples
1074 ///
1075 /// ```
1076 /// # use oracle::Error;
1077 /// # use oracle::test_util;
1078 /// # let conn = test_util::connect()?;
1079 /// let (version, banner) = conn.server_version()?;
1080 /// println!("Oracle Version: {}", version);
1081 /// println!("--- Version Banner ---");
1082 /// println!("{}", banner);
1083 /// println!("---------------------");
1084 /// # Ok::<(), Error>(())
1085 /// ```
1086 pub fn server_version(&self) -> Result<(Version, String)> {
1087 let mut s = OdpiStr::new("");
1088 let mut ver = MaybeUninit::uninit();
1089 chkerr!(
1090 self.ctxt(),
1091 dpiConn_getServerVersion(self.handle(), &mut s.ptr, &mut s.len, ver.as_mut_ptr())
1092 );
1093 Ok((
1094 Version::new_from_dpi_ver(unsafe { ver.assume_init() }),
1095 s.to_string(),
1096 ))
1097 }
1098
1099 /// Changes the password for the specified user
1100 pub fn change_password(
1101 &self,
1102 username: &str,
1103 old_password: &str,
1104 new_password: &str,
1105 ) -> Result<()> {
1106 let username = OdpiStr::new(username);
1107 let old_password = OdpiStr::new(old_password);
1108 let new_password = OdpiStr::new(new_password);
1109 chkerr!(
1110 self.ctxt(),
1111 dpiConn_changePassword(
1112 self.handle(),
1113 username.ptr,
1114 username.len,
1115 old_password.ptr,
1116 old_password.len,
1117 new_password.ptr,
1118 new_password.len
1119 )
1120 );
1121 Ok(())
1122 }
1123
1124 /// Pings the connection to see if it is still alive.
1125 ///
1126 /// It checks the connection by making a network round-trip
1127 /// between the client and the server.
1128 ///
1129 /// See also [`Connection::status`].
1130 pub fn ping(&self) -> Result<()> {
1131 chkerr!(self.ctxt(), dpiConn_ping(self.handle()));
1132 Ok(())
1133 }
1134
1135 /// Gets the status of the connection.
1136 ///
1137 /// It returns `Ok(ConnStatus::Closed)` when the connection was closed
1138 /// by [`Connection::close`].
1139 /// Otherwise see bellow.
1140 ///
1141 /// **Oracle client 12.2 and later:**
1142 ///
1143 /// It checks whether the underlying TCP socket has disconnected
1144 /// by the server. There is no guarantee that the server is alive
1145 /// and the network between the client and server has no trouble.
1146 ///
1147 /// For example, it returns `Ok(ConnStatus::NotConnected)` when the
1148 /// database on the server-side OS stopped and the client received
1149 /// a FIN or RST packet. However it returns `Ok(ConnStatus::Normal)`
1150 /// when the server-side OS itself crashes or the network is in
1151 /// trouble.
1152 ///
1153 /// **Oracle client 11.2 and 12.1:**
1154 ///
1155 /// It returns `Ok(ConnStatus::Normal)` when the last network
1156 /// round-trip between the client and server went through. Otherwise,
1157 /// `Ok(ConnStatus::NotConnected)`. There is no guarantee that the
1158 /// next network round-trip will go through.
1159 ///
1160 /// See also [`Connection::ping`].
1161 pub fn status(&self) -> Result<ConnStatus> {
1162 match self.oci_attr::<ServerStatus>() {
1163 Ok(status) => match status {
1164 OCI_SERVER_NOT_CONNECTED => Ok(ConnStatus::NotConnected),
1165 OCI_SERVER_NORMAL => Ok(ConnStatus::Normal),
1166 _ => Err(Error::internal_error(format!(
1167 "unexpected server status {}",
1168 status
1169 ))),
1170 },
1171 Err(err) if err.dpi_code() == Some(DPI_ERR_NOT_CONNECTED) => Ok(ConnStatus::Closed),
1172 Err(err) => Err(err),
1173 }
1174 }
1175
1176 /// Get the warning when connecting to the database or executing SQL statements.
1177 ///
1178 /// This is available to check the following two cases.
1179 ///
1180 /// 1. The user account status is in grace period of password expiration. See [Password Change Life Cycle].
1181 /// 2. A stored procedure is created with PL/SQL compilation errors.
1182 ///
1183 /// ```no_run
1184 /// # use oracle::{Connection, Error};
1185 /// # use oracle::test_util;
1186 /// # let username = test_util::main_user();
1187 /// # let password = test_util::main_password();
1188 /// # let connect_string = test_util::connect_string();
1189 /// let conn = Connection::connect(username, password, connect_string)?;
1190 ///
1191 /// // Check warning when connecting to the database.
1192 /// // This must be done before any SQL execution. Otherwise it is cleared.
1193 /// if let Some(Error::OciError(dberr)) = conn.last_warning() {
1194 /// if dberr.code() == 28002 {
1195 /// println!("{}", dberr.message()); // => "the password will expire within n days."
1196 /// }
1197 /// }
1198 /// # Ok::<(), Error>(())
1199 /// ```
1200 ///
1201 /// ```
1202 /// # use oracle::{Connector, Error};
1203 /// # use oracle::test_util;
1204 /// # let conn = test_util::connect()?;
1205 /// // create a procedure with compilation error
1206 /// let sql = "create or replace procedure my_proc is begin; null; end;";
1207 /// conn.execute(sql, &[])?;
1208 /// match conn.last_warning() {
1209 /// Some(Error::OciError(dberr)) if dberr.code() == 24344 => (),
1210 /// warn => panic!("Unexpected last warning: {:?}", warn),
1211 /// }
1212 ///
1213 /// // create a procedure without compilation error
1214 /// let sql = "create or replace procedure my_proc is begin null; end;";
1215 /// conn.execute(sql, &[])?;
1216 /// // The last warning is cleared when a SQL statement is executed successfully without any warning.
1217 /// match conn.last_warning() {
1218 /// None => (),
1219 /// warn => panic!("Unexpected last warning: {:?}", warn),
1220 /// }
1221 /// # Ok::<(), Error>(())
1222 /// ```
1223 ///
1224 /// [Password Change Life Cycle]: https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-BED74427-BC63-4095-8829-1AF68411FC6B
1225 pub fn last_warning(&self) -> Option<Error> {
1226 self.ctxt().last_warning().map(Error::oci_error)
1227 }
1228
1229 /// Gets the statement cache size
1230 ///
1231 /// See also [`Connector::stmt_cache_size`]
1232 pub fn stmt_cache_size(&self) -> Result<u32> {
1233 let mut size = 0u32;
1234 chkerr!(
1235 self.ctxt(),
1236 dpiConn_getStmtCacheSize(self.handle(), &mut size)
1237 );
1238 Ok(size)
1239 }
1240
1241 /// Sets the statement cache size
1242 ///
1243 /// See also [`Connector::stmt_cache_size`]
1244 pub fn set_stmt_cache_size(&self, size: u32) -> Result<()> {
1245 chkerr!(self.ctxt(), dpiConn_setStmtCacheSize(self.handle(), size));
1246 Ok(())
1247 }
1248
1249 /// Gets the current call timeout used for round-trips to
1250 /// the database made with this connection. `None` means that no timeouts
1251 /// will take place.
1252 pub fn call_timeout(&self) -> Result<Option<Duration>> {
1253 let mut value = 0;
1254 chkerr!(
1255 self.ctxt(),
1256 dpiConn_getCallTimeout(self.handle(), &mut value)
1257 );
1258 if value != 0 {
1259 Ok(Some(Duration::from_millis(value.into())))
1260 } else {
1261 Ok(None)
1262 }
1263 }
1264
1265 /// Sets the call timeout to be used for round-trips to the
1266 /// database made with this connection. None means that no timeouts
1267 /// will take place.
1268 ///
1269 /// The call timeout value applies to each database round-trip
1270 /// individually, not to the sum of all round-trips. Time spent
1271 /// processing in rust-oracle before or after the completion of each
1272 /// round-trip is not counted.
1273 ///
1274 /// - If the time from the start of any one round-trip to the
1275 /// completion of that same round-trip exceeds call timeout,
1276 /// then the operation is halted and an exception occurs.
1277 ///
1278 /// - In the case where an rust-oracle operation requires more than one
1279 /// round-trip and each round-trip takes less than call timeout,
1280 /// then no timeout will occur, even if the sum of all round-trip
1281 /// calls exceeds call timeout.
1282 ///
1283 /// - If no round-trip is required, the operation will never be
1284 /// interrupted.
1285 ///
1286 /// After a timeout is triggered, rust-oracle attempts to clean up the
1287 /// internal connection state. The cleanup is allowed to take another
1288 /// `duration`.
1289 ///
1290 /// If the cleanup was successful, an exception DPI-1067 will be
1291 /// raised but the application can continue to use the connection.
1292 ///
1293 /// For small values of call timeout, the connection cleanup may not
1294 /// complete successfully within the additional call timeout
1295 /// period. In this case an exception ORA-3114 is raised and the
1296 /// connection will no longer be usable. It should be closed.
1297 ///
1298 /// # Examples
1299 ///
1300 /// ```
1301 /// # use oracle::Error;
1302 /// # use oracle::test_util;
1303 /// # use std::time::{Duration, Instant};
1304 /// # let conn = test_util::connect()?;
1305 /// // Set timeout three seconds.
1306 /// conn.set_call_timeout(Some(Duration::from_millis(3_000)))?;
1307 ///
1308 /// let now = Instant::now();
1309 /// let range = Duration::from_millis(2_900)..=Duration::from_millis(20_000);
1310 ///
1311 /// // This query is canceled by timeout.
1312 /// let result = conn.query_row_as::<u64>("select count(*) from all_objects, all_objects, all_objects, all_objects, all_objects", &[]);
1313 /// assert!(result.is_err());
1314 /// let elapsed = now.elapsed();
1315 /// assert!(range.contains(&elapsed), "cancel: {:?}, {:?}", elapsed, result.unwrap_err());
1316 /// # Ok::<(), Error>(())
1317 /// ```
1318 pub fn set_call_timeout(&self, dur: Option<Duration>) -> Result<()> {
1319 if let Some(dur) = dur {
1320 let msecs = dur.as_millis().try_into().map_err(|_| {
1321 Error::out_of_range(format!(
1322 "too long duration {:?}. It must be less than 49.7 days",
1323 dur
1324 ))
1325 })?;
1326 if msecs == 0 {
1327 return Err(Error::out_of_range(format!(
1328 "too short duration {:?}. It must not be submilliseconds",
1329 dur
1330 )));
1331 }
1332 chkerr!(self.ctxt(), dpiConn_setCallTimeout(self.handle(), msecs));
1333 } else {
1334 chkerr!(self.ctxt(), dpiConn_setCallTimeout(self.handle(), 0));
1335 }
1336 Ok(())
1337 }
1338
1339 /// Gets current schema associated with the connection
1340 pub fn current_schema(&self) -> Result<String> {
1341 let mut s = OdpiStr::new("");
1342 chkerr!(
1343 self.ctxt(),
1344 dpiConn_getCurrentSchema(self.handle(), &mut s.ptr, &mut s.len)
1345 );
1346 Ok(s.to_string())
1347 }
1348
1349 /// Sets current schema associated with the connection
1350 ///
1351 /// `conn.set_current_schema("MDSYS")` has same effect with the following SQL.
1352 ///
1353 /// ```sql
1354 /// ALTER SESSION SET CURRENT_SCHEMA = MDSYS;
1355 /// ```
1356 ///
1357 /// # Examples
1358 ///
1359 /// ```
1360 /// # use oracle::Error;
1361 /// # use oracle::test_util;
1362 /// # let conn1 = test_util::connect()?;
1363 /// # let conn2 = test_util::connect()?;
1364 ///
1365 /// // Get the username and sid of connection 1.
1366 /// let (username, sid) = conn1.query_row_as::<(String, i32)>("select user, sys_context('userenv', 'sid') from dual", &[])?;
1367 ///
1368 /// // Create a closure to get the schema name of connection 1 in the database side using connection 2.
1369 /// let mut stmt = conn2.statement("select schemaname from v$session where sid = :1").build()?;
1370 /// let mut schema_name = move || { stmt.query_row_as::<String>(&[&sid]) };
1371 ///
1372 /// // The default current schema is same with the username.
1373 /// assert_eq!(schema_name()?, username);
1374 ///
1375 /// // Change the current schema of connection 1.
1376 /// let new_schema_name = "MDSYS";
1377 /// conn1.set_current_schema(new_schema_name)?;
1378 ///
1379 /// // The current schema of connection 1 in the database side has not been changed yet.
1380 /// assert_eq!(schema_name()?, username);
1381 ///
1382 /// // Call a function sending packets to the database server.
1383 /// // The new schema name is prepended to the packets.
1384 /// let _ = conn1.query_row_as::<i32>("select 1 from dual", &[]);
1385 ///
1386 /// // The current schema of connection 1 in the database side is changed.
1387 /// assert_eq!(schema_name()?, new_schema_name);
1388 ///
1389 /// # Ok::<(), Error>(())
1390 /// ```
1391 pub fn set_current_schema(&self, current_schema: &str) -> Result<()> {
1392 let s = OdpiStr::new(current_schema);
1393 chkerr!(
1394 self.ctxt(),
1395 dpiConn_setCurrentSchema(self.handle(), s.ptr, s.len)
1396 );
1397 Ok(())
1398 }
1399
1400 /// Gets edition associated with the connection
1401 pub fn edition(&self) -> Result<String> {
1402 let mut s = OdpiStr::new("");
1403 chkerr!(
1404 self.ctxt(),
1405 dpiConn_getEdition(self.handle(), &mut s.ptr, &mut s.len)
1406 );
1407 Ok(s.to_string())
1408 }
1409
1410 /// Gets external name associated with the connection
1411 pub fn external_name(&self) -> Result<String> {
1412 let mut s = OdpiStr::new("");
1413 chkerr!(
1414 self.ctxt(),
1415 dpiConn_getExternalName(self.handle(), &mut s.ptr, &mut s.len)
1416 );
1417 Ok(s.to_string())
1418 }
1419
1420 /// Sets external name associated with the connection
1421 pub fn set_external_name(&self, external_name: &str) -> Result<()> {
1422 let s = OdpiStr::new(external_name);
1423 chkerr!(
1424 self.ctxt(),
1425 dpiConn_setExternalName(self.handle(), s.ptr, s.len)
1426 );
1427 Ok(())
1428 }
1429
1430 /// Gets internal name associated with the connection
1431 pub fn internal_name(&self) -> Result<String> {
1432 let mut s = OdpiStr::new("");
1433 chkerr!(
1434 self.ctxt(),
1435 dpiConn_getInternalName(self.handle(), &mut s.ptr, &mut s.len)
1436 );
1437 Ok(s.to_string())
1438 }
1439
1440 /// Sets internal name associated with the connection
1441 pub fn set_internal_name(&self, internal_name: &str) -> Result<()> {
1442 let s = OdpiStr::new(internal_name);
1443 chkerr!(
1444 self.ctxt(),
1445 dpiConn_setInternalName(self.handle(), s.ptr, s.len)
1446 );
1447 Ok(())
1448 }
1449
1450 /// Sets module associated with the connection
1451 ///
1452 /// This is same with calling [DBMS_APPLICATION_INFO.SET_MODULE][] but
1453 /// without executing a statement. The module name is piggybacked
1454 /// to the server with the next network round-trip.
1455 ///
1456 /// [DBMS_APPLICATION_INFO.SET_MODULE]: https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-B2E2BD20-D91D-40DB-A3F6-37A853384F30
1457 pub fn set_module(&self, module: &str) -> Result<()> {
1458 let s = OdpiStr::new(module);
1459 chkerr!(self.ctxt(), dpiConn_setModule(self.handle(), s.ptr, s.len));
1460 Ok(())
1461 }
1462
1463 /// Sets action associated with the connection
1464 ///
1465 /// This is same with calling [DBMS_APPLICATION_INFO.SET_ACTION][] but
1466 /// without executing a statement. The action name is piggybacked
1467 /// to the server with the next network round-trip.
1468 ///
1469 /// [DBMS_APPLICATION_INFO.SET_ACTION]: https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-90DA860F-BFBE-4539-BA00-2279B02B8F26
1470 pub fn set_action(&self, action: &str) -> Result<()> {
1471 let s = OdpiStr::new(action);
1472 chkerr!(self.ctxt(), dpiConn_setAction(self.handle(), s.ptr, s.len));
1473 Ok(())
1474 }
1475
1476 /// Sets client info associated with the connection
1477 ///
1478 /// This is same with calling [DBMS_APPLICATION_INFO.SET_CLIENT_INFO][] but
1479 /// without executing a statement. The client info is piggybacked
1480 /// to the server with the next network round-trip.
1481 ///
1482 /// [DBMS_APPLICATION_INFO.SET_CLIENT_INFO]: https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-68A3DF04-BE91-46CC-8D2B-97BA0E89956F
1483 pub fn set_client_info(&self, client_info: &str) -> Result<()> {
1484 let s = OdpiStr::new(client_info);
1485 chkerr!(
1486 self.ctxt(),
1487 dpiConn_setClientInfo(self.handle(), s.ptr, s.len)
1488 );
1489 Ok(())
1490 }
1491
1492 /// Sets client identifier associated with the connection
1493 ///
1494 /// This is same with calling [DBMS_SESSION.SET_IDENTIFIER][] but
1495 /// without executing a statement. The client identifier is piggybacked
1496 /// to the server with the next network round-trip.
1497 ///
1498 /// [DBMS_SESSION.SET_IDENTIFIER]: https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-988EA930-BDFE-4205-A806-E54F05333562
1499 pub fn set_client_identifier(&self, client_identifier: &str) -> Result<()> {
1500 let s = OdpiStr::new(client_identifier);
1501 chkerr!(
1502 self.ctxt(),
1503 dpiConn_setClientIdentifier(self.handle(), s.ptr, s.len)
1504 );
1505 Ok(())
1506 }
1507
1508 /// Sets name of the database operation to be monitored in the database.
1509 /// Sets to `''` if you want to end monitoring the current running database operation.
1510 ///
1511 /// This is same with calling [DBMS_SQL_MONITOR.BEGIN_OPERATION][] but
1512 /// without executing a statement. The database operation name is piggybacked
1513 /// to the server with the next network round-trip.
1514 ///
1515 /// See [Monitoring Database Operations][] in Oracle Database SQL Tuning Guide
1516 ///
1517 /// [db_op]: https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-9CE3C342-D210-4690-A7E9-5813EF9D558E
1518 /// [DBMS_SQL_MONITOR.BEGIN_OPERATION]: https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-25BE0E79-3A19-4303-9F66-2CFDB87C7F82
1519 /// [Monitoring Database Operations]: https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-C941CE9D-97E1-42F8-91ED-4949B2B710BF
1520 pub fn set_db_op(&self, db_op: &str) -> Result<()> {
1521 let s = OdpiStr::new(db_op);
1522 chkerr!(self.ctxt(), dpiConn_setDbOp(self.handle(), s.ptr, s.len));
1523 Ok(())
1524 }
1525
1526 /// Starts up a database
1527 ///
1528 /// This corresponds to sqlplus command `startup nomount`.
1529 /// You need to connect the databas as system privilege in prelim_auth
1530 /// mode in advance.
1531 /// After this method is executed, you need to reconnect the server
1532 /// as system privilege *without* prelim_auth and executes
1533 /// `alter database mount` and then `alter database open`.
1534 ///
1535 /// # Examples
1536 ///
1537 /// Connect to an idle instance as sysdba and start up a database
1538 ///
1539 /// ```no_run
1540 /// # use oracle::*;
1541 /// // connect as sysdba with prelim_auth mode
1542 /// let conn = Connector::new("sys", "change_on_install", "")
1543 /// .privilege(Privilege::Sysdba)
1544 /// .prelim_auth(true)
1545 /// .connect()?;
1546 ///
1547 /// // start the instance
1548 /// conn.startup_database(&[])?;
1549 /// conn.close()?;
1550 ///
1551 /// // connect again without prelim_auth
1552 /// let conn = Connector::new("sys", "change_on_install", "")
1553 /// .privilege(Privilege::Sysdba)
1554 /// .connect()?;
1555 ///
1556 /// // mount and open a database
1557 /// conn.execute("alter database mount", &[])?;
1558 /// conn.execute("alter database open", &[])?;
1559 /// # Ok::<(), Error>(())
1560 /// ```
1561 ///
1562 /// Start up a database in restricted mode
1563 ///
1564 /// ```ignore
1565 /// ...
1566 /// conn.startup_database(&[StartupMode::Restrict])?;
1567 /// ...
1568 /// ```
1569 ///
1570 /// If the database is running, shut it down with mode ABORT and then
1571 /// start up in restricted mode
1572 ///
1573 /// ```ignore
1574 /// ...
1575 /// conn.startup_database(&[StartupMode::Force, StartupMode::Restrict])?;
1576 /// ...
1577 /// ```
1578 pub fn startup_database(&self, modes: &[StartupMode]) -> Result<()> {
1579 let mut mode_num = 0;
1580 for mode in modes {
1581 mode_num |= match *mode {
1582 StartupMode::Force => DPI_MODE_STARTUP_FORCE,
1583 StartupMode::Restrict => DPI_MODE_STARTUP_RESTRICT,
1584 };
1585 }
1586 chkerr!(
1587 self.ctxt(),
1588 dpiConn_startupDatabase(self.handle(), mode_num)
1589 );
1590 Ok(())
1591 }
1592
1593 /// Shuts down a database
1594 ///
1595 /// When this method is called with [`ShutdownMode::Default`],
1596 /// [`ShutdownMode::Transactional`], [`ShutdownMode::TransactionalLocal`]
1597 /// or [`ShutdownMode::Immediate`], execute "alter database close normal"
1598 /// and "alter database dismount" and call this method again with
1599 /// [`ShutdownMode::Final`].
1600 ///
1601 /// When this method is called with [`ShutdownMode::Abort`],
1602 /// the database is aborted immediately.
1603 ///
1604 /// # Examples
1605 ///
1606 /// Same with `shutdown immediate` on sqlplus.
1607 ///
1608 /// ```no_run
1609 /// # use oracle::*;
1610 /// // connect as sysdba
1611 /// let conn = Connector::new("sys", "change_on_install", "")
1612 /// .privilege(Privilege::Sysdba)
1613 /// .connect()?;
1614 ///
1615 /// // begin 'shutdown immediate'
1616 /// conn.shutdown_database(ShutdownMode::Immediate)?;
1617 ///
1618 /// // close and dismount the database
1619 /// conn.execute("alter database close normal", &[])?;
1620 /// conn.execute("alter database dismount", &[])?;
1621 ///
1622 /// // finish shutdown
1623 /// conn.shutdown_database(ShutdownMode::Final)?;
1624 /// # Ok::<(), Error>(())
1625 /// ```
1626 ///
1627 /// Same with `shutdown abort` on sqlplus.
1628 ///
1629 /// ```no_run
1630 /// # use oracle::*;
1631 /// // connect as sysdba
1632 /// let conn = Connector::new("sys", "change_on_install", "")
1633 /// .privilege(Privilege::Sysdba).connect()?;
1634 ///
1635 /// // 'shutdown abort'
1636 /// conn.shutdown_database(ShutdownMode::Abort)?;
1637 ///
1638 /// // The database is aborted here.
1639 /// # Ok::<(), Error>(())
1640 /// ```
1641 pub fn shutdown_database(&self, mode: ShutdownMode) -> Result<()> {
1642 let mode = match mode {
1643 ShutdownMode::Default => DPI_MODE_SHUTDOWN_DEFAULT,
1644 ShutdownMode::Transactional => DPI_MODE_SHUTDOWN_TRANSACTIONAL,
1645 ShutdownMode::TransactionalLocal => DPI_MODE_SHUTDOWN_TRANSACTIONAL_LOCAL,
1646 ShutdownMode::Immediate => DPI_MODE_SHUTDOWN_IMMEDIATE,
1647 ShutdownMode::Abort => DPI_MODE_SHUTDOWN_ABORT,
1648 ShutdownMode::Final => DPI_MODE_SHUTDOWN_FINAL,
1649 };
1650 chkerr!(self.ctxt(), dpiConn_shutdownDatabase(self.handle(), mode));
1651 Ok(())
1652 }
1653
1654 /// Gets the tag of the connection that was acquired from a connection pool.
1655 /// It is `""` if the connection is a standalone one or not tagged.
1656 ///
1657 /// # Examples
1658 ///
1659 /// ```
1660 /// # use oracle::{Connection, Error};
1661 /// # use oracle::pool::{PoolBuilder, PoolOptions};
1662 /// # use oracle::conn;
1663 /// # use oracle::test_util;
1664 /// # let username = test_util::main_user();
1665 /// # let username = &username;
1666 /// # let password = test_util::main_password();
1667 /// # let password = &password;
1668 /// # let connect_string = test_util::connect_string();
1669 /// # let connect_string = &connect_string;
1670 ///
1671 /// // standalone connection
1672 /// let conn = Connection::connect(username, password, connect_string)?;
1673 /// assert_eq!(conn.tag(), "");
1674 /// assert_eq!(conn.tag_found(), false);
1675 ///
1676 /// let pool = PoolBuilder::new(username, password, connect_string)
1677 /// .build()?;
1678 /// let opts = PoolOptions::new().tag("NAME=VALUE");
1679 ///
1680 /// // No connections with tag "NAME=VALUE" exist in the pool at first.
1681 /// let conn = pool.get_with_options(&opts)?;
1682 /// assert_eq!(conn.tag(), "");
1683 /// assert_eq!(conn.tag_found(), false);
1684 /// // Close the connection with setting a new tag.
1685 /// conn.close_with_mode(conn::CloseMode::Retag("NAME=VALUE"))?;
1686 ///
1687 /// // One connection with tag "NAME=VALUE" exists in the pool now.
1688 /// let conn = pool.get_with_options(&opts)?;
1689 /// assert_eq!(conn.tag_found(), true);
1690 /// assert_eq!(conn.tag(), "NAME=VALUE");
1691 /// # Ok::<(), Error>(())
1692 /// ```
1693 pub fn tag(&self) -> &str {
1694 &self.conn.tag
1695 }
1696
1697 /// Gets `true` when the connection is a standalone one
1698 /// or it is a connection with the specified tag by
1699 /// [`PoolOptions::tag`].
1700 ///
1701 /// Sea also [`Connection::tag`].
1702 pub fn tag_found(&self) -> bool {
1703 self.conn.tag_found
1704 }
1705
1706 /// Returns `true` when the connection is a standalone one
1707 /// or a newly created one by a connection pool.
1708 ///
1709 /// # Examples
1710 ///
1711 /// ```
1712 /// # use oracle::Connection;
1713 /// # use oracle::Error;
1714 /// # use oracle::pool::PoolBuilder;
1715 /// # use oracle::test_util;
1716 /// # let username = test_util::main_user();
1717 /// # let username = &username;
1718 /// # let password = test_util::main_password();
1719 /// # let password = &password;
1720 /// # let connect_string = test_util::connect_string();
1721 /// # let connect_string = &connect_string;
1722 ///
1723 /// let conn = Connection::connect(username, password, connect_string)?;
1724 /// assert!(conn.is_new_connection(), "standalone connection");
1725 ///
1726 /// let pool = PoolBuilder::new(username, password, connect_string)
1727 /// .build()?;
1728 ///
1729 /// let conn = pool.get()?; // Get a newly created connection
1730 /// assert!(conn.is_new_connection(), "new connectoin from the pool");
1731 /// conn.close()?; // Back the connection to the pool
1732 ///
1733 /// let conn = pool.get()?; // Get a connection cached in the pool
1734 /// assert!(!conn.is_new_connection(), "cached connectoin in the pool");
1735 /// conn.close()?;
1736 /// # Ok::<(), Error>(())
1737 /// ```
1738 pub fn is_new_connection(&self) -> bool {
1739 self.conn.is_new_connection
1740 }
1741
1742 /// Returns information about the connection
1743 ///
1744 /// # Examples
1745 ///
1746 /// ```
1747 /// # use oracle::Error;
1748 /// # use oracle::test_util;
1749 /// # let conn = test_util::connect()?;
1750 /// let info = conn.info()?;
1751 /// let instance_name = conn.query_row_as::<String>("SELECT SYS_CONTEXT('USERENV', 'INSTANCE_NAME') FROM DUAL", &[])?;
1752 /// assert!(info.instance_name.eq_ignore_ascii_case(&instance_name));
1753 /// let service_name = conn.query_row_as::<String>("SELECT SYS_CONTEXT('USERENV', 'SERVICE_NAME') FROM DUAL", &[])?;
1754 /// assert_eq!(info.service_name, service_name);
1755 /// # Ok::<(), Error>(())
1756 /// ```
1757 pub fn info(&self) -> Result<Info> {
1758 let mut info = MaybeUninit::uninit();
1759 chkerr!(
1760 self.ctxt(),
1761 dpiConn_getInfo(self.handle(), info.as_mut_ptr())
1762 );
1763 Info::from_dpi(unsafe { &info.assume_init() })
1764 }
1765
1766 /// Gets an OCI handle attribute corresponding to the specified type parameter
1767 /// See the [`oci_attr` module][crate::oci_attr] for details.
1768 pub fn oci_attr<T>(&self) -> Result<<<T::DataType as DataType>::Type as ToOwned>::Owned>
1769 where
1770 T: OciAttr,
1771 T::HandleType: ConnHandle,
1772 T::Mode: ReadMode,
1773 {
1774 let attr_value = AttrValue::from_conn(self, <T::HandleType>::HANDLE_TYPE, <T>::ATTR_NUM);
1775 unsafe { <T::DataType>::get(attr_value) }
1776 }
1777
1778 /// Sets an OCI handle attribute corresponding to the specified type parameter
1779 /// See the [`oci_attr` module][crate::oci_attr] for details.
1780 pub fn set_oci_attr<T>(&mut self, value: &<T::DataType as DataType>::Type) -> Result<()>
1781 where
1782 T: OciAttr,
1783 T::HandleType: ConnHandle,
1784 T::Mode: WriteMode,
1785 {
1786 let mut attr_value =
1787 AttrValue::from_conn(self, <T::HandleType>::HANDLE_TYPE, <T>::ATTR_NUM);
1788 unsafe { <T::DataType>::set(&mut attr_value, value) }
1789 }
1790}
1791
1792impl Drop for Connection {
1793 fn drop(&mut self) {
1794 let _ = self.clear_object_type_cache();
1795 }
1796}
1797
1798impl fmt::Debug for Connection {
1799 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1800 write!(f, "Connection {{ conn: {:?}", self.conn)
1801 }
1802}