oracle/oci_attr/
mod.rs

1// Rust-oracle - Rust binding for Oracle database
2//
3// URL: https://github.com/kubo/rust-oracle
4//
5//-----------------------------------------------------------------------------
6// Copyright (c) 2017-2021 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
16//! Rust-oracle is based on ODPI-C using Oracle Call Interface (OCI) internally.
17//! OCI treats resources as handles, which have various attributes documented [here].
18//!
19//! The module defines type parameters to access some OCI attributes and
20//! the trait [`OciAttr`] to define your own type parameters to access attributes
21//! which are not predefined in this module.
22//!
23//! [here]: https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-CB59C987-07E7-42D4-ADDF-96142CBD3D11
24use crate::oci_attr::data_type::{DataType, DurationUsecU64, MaxStringSize};
25#[cfg(any(doc, test))]
26use crate::oci_attr::handle::Server;
27use crate::oci_attr::handle::{HandleType, Session, Stmt, SvcCtx};
28#[cfg(any(doc, test))]
29use crate::oci_attr::mode::Write;
30use crate::oci_attr::mode::{Mode, Read, ReadWrite};
31#[cfg(doc)]
32use crate::Connection;
33#[cfg(doc)]
34use std::time::Duration;
35
36pub mod data_type;
37pub mod handle;
38pub mod mode;
39
40#[allow(clippy::missing_safety_doc)]
41pub unsafe trait OciAttr {
42    /// [`SvcCtx`], [`Session`], [`Server`] or [`Stmt`].
43    /// Other handle and descriptor types are unsupported.
44    type HandleType: HandleType;
45
46    /// [`Read`], [`Write`] or [`ReadWrite`]
47    type Mode: Mode;
48
49    /// Attribute data type
50    ///
51    /// The following table is the mapping between basic data types. If
52    /// incorrect types are specified, the behavior is undefined.
53    ///
54    /// Types in Oracle manual | Rust type
55    /// ---|---
56    /// `ub1*`/`ub1` | [`u8`]
57    /// `ub2*`/`ub2` | [`u16`]
58    /// `ub4*`/`ub4` | [`u32`]
59    /// `ub8*`/`ub8` | [`u64`]
60    /// `boolean*`/`boolean` | [`bool`]
61    /// pointer types such as `OCISession**`/`OCISession*` | `*mut c_void`
62    /// `oratext**`/`oratext*` | [`str`] [^str]
63    /// `ub1*`(with length; value is copied; is really a ub1 array) | `[u8]` [^u8slice]
64    ///
65    /// The following table is the mapping of predefined types based on basic data types.
66    /// They are designed for specific attributes.
67    ///
68    /// Types in Oracle manual | Rust type
69    /// ---|---
70    /// `ub8*` | [`DurationUsecU64`] which gets u64 values representing microsecods as [`Duration`]
71    /// `ub1*` | [`MaxStringSize`] which gets ub1 values as variants of [`MaxStringSize`]
72    ///
73    /// Look at the source code of [`DurationUsecU64`] and [`MaxStringSize`] as samples
74    /// when you need to implement your own data types.
75    ///
76    /// [^str]: Values are got as [`String`] because [`str`] implements [`ToOwned`] whose associate type `Owned` is [`String`].
77    ///
78    /// [^u8slice]: Values are got as `Vec<u8>` because `[T]` where T: Clone implements [`ToOwned`] whose associate type `Owned` is `Vec<T>`.
79    ///
80    type DataType: DataType + ?Sized;
81
82    /// Attribute number defined in `oci.h` included in Oracle Instant Client SDK
83    const ATTR_NUM: u32;
84}
85
86/// A type parameter for [`Connection::oci_attr`] to get [`OCI_ATTR_VARTYPE_MAXLEN_COMPAT`] as [`MaxStringSize`].
87/// which controls the maximum size of `VARCHAR2`, `NVARCHAR` and `RAW`.
88///
89/// This corresponds to the result of the following SQL statement when
90/// a database user has a privilege to access `v$parameter`.
91///
92/// ```sql
93/// select value from v$parameter where name = 'max_string_size'
94/// ```
95///
96/// [`OCI_ATTR_VARTYPE_MAXLEN_COMPAT`]: https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-D8EE68EB-7E38-4068-B06E-DF5686379E5E__GUID-2EFB39BC-6131-4EAD-BBF6-7CA8E5F2BBF4
97#[derive(Debug)]
98pub struct VarTypeMaxLenCompat;
99const OCI_ATTR_VARTYPE_MAXLEN_COMPAT: u32 = 489;
100unsafe impl OciAttr for VarTypeMaxLenCompat {
101    type HandleType = SvcCtx;
102    type Mode = Read;
103    type DataType = MaxStringSize;
104    const ATTR_NUM: u32 = OCI_ATTR_VARTYPE_MAXLEN_COMPAT;
105}
106
107/// A type parameter for [`Connection::oci_attr`] to get [`OCI_ATTR_CALL_TIME`] as [`Duration`][],
108/// which is the server-side time for the preceding call
109///
110/// Set `true` to [`CollectCallTime`] in advance.
111///
112/// # Examples
113///
114/// ```
115/// # use oracle::Error;
116/// # use oracle::test_util;
117/// use oracle::oci_attr::CallTime;
118/// use oracle::oci_attr::CollectCallTime;
119/// use std::time::Duration;
120/// # let mut conn = test_util::connect()?;
121/// # if !test_util::check_version(&conn, &test_util::VER11_2, &test_util::VER18)? {
122/// #     return Ok(());
123/// # }
124///
125/// // Enable CollectCallTime
126/// conn.set_oci_attr::<CollectCallTime>(&true)?;
127///
128/// // This SQL consumes one second in the server-side.
129/// conn.execute("begin dbms_session.sleep(1); end;", &[])?;
130/// let call_time = conn.oci_attr::<CallTime>()?;
131/// assert!(call_time >= Duration::from_secs(1), "call_time is {:?}.", call_time);
132/// # Ok::<(), Error>(())
133/// ```
134///
135/// [`OCI_ATTR_CALL_TIME`]: https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-FB263210-118E-4DB3-A840-1769EF0CB977__GUID-AA22FE4F-8942-4819-B01F-068DCEAE9B72
136/// [`Duration`]: std::time::Duration
137pub struct CallTime;
138const OCI_ATTR_CALL_TIME: u32 = 370;
139unsafe impl OciAttr for CallTime {
140    type HandleType = Session;
141    type Mode = Read;
142    type DataType = DurationUsecU64;
143    const ATTR_NUM: u32 = OCI_ATTR_CALL_TIME;
144}
145
146/// A type parameter for [`Connection::oci_attr`] and [`Connection::set_oci_attr`] to get and set [`OCI_ATTR_COLLECT_CALL_TIME`],
147/// which causes the server to measure call time for each subsequent OCI call
148///
149/// See [`CallTime`].
150///
151/// [`OCI_ATTR_COLLECT_CALL_TIME`]: https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-FB263210-118E-4DB3-A840-1769EF0CB977__GUID-D4B6CBB6-5627-474C-ABE6-F2CE694DE62B
152pub struct CollectCallTime;
153const OCI_ATTR_COLLECT_CALL_TIME: u32 = 369;
154unsafe impl OciAttr for CollectCallTime {
155    type HandleType = Session;
156    type Mode = ReadWrite;
157    type DataType = bool;
158    const ATTR_NUM: u32 = OCI_ATTR_COLLECT_CALL_TIME;
159}
160
161/// A type parameter for [`Connection::oci_attr`] and [`Connection::set_oci_attr`] to get and set [`OCI_ATTR_DEFAULT_LOBPREFETCH_SIZE`],
162/// which specifies the default prefetch buffer size for each LOB locator
163///
164/// # Examples
165///
166/// ```
167/// # use oracle::Error;
168/// # use oracle::test_util;
169/// # use oracle::sql_type::Clob;
170/// use oracle::oci_attr::DefaultLobPrefetchSize;
171/// # let mut conn = test_util::connect()?;
172///
173/// let lob_size = 64 * 1024;
174/// conn.set_oci_attr::<DefaultLobPrefetchSize>(&lob_size)?;
175///
176/// # conn.execute("insert into TestCLOBs values (1, '11111111111111111111111111111')", &[])?;
177/// let mut stmt = conn
178///     .statement("select CLOBCol from TestCLOBs where IntCol = :1")
179///     .lob_locator()
180///     .build()?;
181/// let lob = stmt.query_row_as::<Clob>(&[&1])?;
182/// # Ok::<(), Error>(())
183/// ```
184///
185/// [`OCI_ATTR_DEFAULT_LOBPREFETCH_SIZE`]: https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-FB263210-118E-4DB3-A840-1769EF0CB977__GUID-13400E7D-C1E9-49AB-AD9B-132CBF11E16C
186pub struct DefaultLobPrefetchSize;
187const OCI_ATTR_DEFAULT_LOBPREFETCH_SIZE: u32 = 438;
188unsafe impl OciAttr for DefaultLobPrefetchSize {
189    type HandleType = Session;
190    type Mode = ReadWrite;
191    type DataType = u32;
192    const ATTR_NUM: u32 = OCI_ATTR_DEFAULT_LOBPREFETCH_SIZE;
193}
194
195/// A type parameter for [`Connection::oci_attr`] to get [`OCI_ATTR_MAX_OPEN_CURSORS`],
196/// which is the maximum number of SQL statements that can be opened in one session
197///
198/// This returns the same value with the result of the following SQL statement when
199/// a database user has a privilege to access `v$parameter`.
200///
201/// ```sql
202/// select value from v$parameter where name = 'open_cursors'
203/// ```
204///
205/// Note that this attribute returns a proper value only when connected to a 12.1 server or later.
206///
207/// [`OCI_ATTR_MAX_OPEN_CURSORS`]: https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-FB263210-118E-4DB3-A840-1769EF0CB977__GUID-0F30D36A-E9E5-4CDB-BF53-C2C876C09E00
208pub struct MaxOpenCursors;
209const OCI_ATTR_MAX_OPEN_CURSORS: u32 = 471;
210unsafe impl OciAttr for MaxOpenCursors {
211    type HandleType = Session;
212    type Mode = Read;
213    type DataType = u32;
214    const ATTR_NUM: u32 = OCI_ATTR_MAX_OPEN_CURSORS;
215}
216
217/// A type parameter for [`Connection::oci_attr`] to get [`OCI_ATTR_TRANSACTION_IN_PROGRESS`] as `bool`,
218/// which indicates whether the connection has a currently active transaction.
219///
220/// Note that this requires Oracle client 12.1 or later.
221///
222/// # Examples
223///
224/// ```
225/// # use oracle::Error;
226/// # use oracle::Version;
227/// # use oracle::test_util;
228/// use oracle::oci_attr::TransactionInProgress;
229/// # if Version::client()? < test_util::VER12_1 {
230/// #     return Ok(());
231/// # }
232/// # let mut conn = test_util::connect()?;
233/// # conn.execute("drop table test_sql_fn_code purge", &[]);
234///
235/// // no active transaction at first
236/// assert_eq!(conn.oci_attr::<TransactionInProgress>()?, false);
237///
238/// // start a transaction
239/// conn.execute("insert into TestTempTable values (1, 'val1')", &[])?;
240/// assert_eq!(conn.oci_attr::<TransactionInProgress>()?, true);
241///
242/// // rollback the transction
243/// conn.rollback()?;
244/// assert_eq!(conn.oci_attr::<TransactionInProgress>()?, false);
245/// # Ok::<(), Error>(())
246/// ```
247/// [`OCI_ATTR_TRANSACTION_IN_PROGRESS`]: https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-FB263210-118E-4DB3-A840-1769EF0CB977__GUID-BCECC9A1-5B02-428F-8A1D-20C9A7997AE5
248pub struct TransactionInProgress;
249const OCI_ATTR_TRANSACTION_IN_PROGRESS: u32 = 484;
250unsafe impl OciAttr for TransactionInProgress {
251    type HandleType = Session;
252    type Mode = Read;
253    type DataType = bool;
254    const ATTR_NUM: u32 = OCI_ATTR_TRANSACTION_IN_PROGRESS;
255}
256
257/// A type parameter for [`Statement::oci_attr`] to get [`OCI_ATTR_SQLFNCODE`],
258/// which is the function code of the SQL command associated with the statement.
259///
260/// Note that the attribute must be read after the statement is executed.
261///
262/// # Examples
263///
264/// ```
265/// # use oracle::Error;
266/// # use oracle::test_util;
267/// use oracle::oci_attr::SqlFnCode;
268/// # use std::thread::sleep;
269/// # use std::time::Duration;
270/// # let mut conn = test_util::connect()?;
271///
272/// let stmt = conn.execute("insert into TestNumbers values(11, 12, 13, 14, 15)", &[])?;
273/// assert_eq!(stmt.oci_attr::<SqlFnCode>()?, 3);
274///
275/// let stmt = conn.execute("update TestNumbers set NumberCol = 13 where IntCol = 11", &[])?;
276/// assert_eq!(stmt.oci_attr::<SqlFnCode>()?, 5);
277///
278/// let stmt = conn.execute("delete TestNumbers where IntCol = 11", &[])?;
279/// assert_eq!(stmt.oci_attr::<SqlFnCode>()?, 9);
280///
281/// # conn.rollback()?;
282/// # Ok::<(), Error>(())
283/// ```
284///
285/// [`Statement::oci_attr`]: crate::Statement::oci_attr
286/// [`OCI_ATTR_SQLFNCODE`]: https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-A251CF91-EB9F-4DBC-8BB8-FB5EA92C20DE__GUID-9E3D8A93-DF13-4023-8444-3F06131D26FB
287pub struct SqlFnCode;
288const OCI_ATTR_SQLFNCODE: u32 = 10;
289unsafe impl OciAttr for SqlFnCode {
290    type HandleType = Stmt;
291    type Mode = Read;
292    type DataType = u16;
293    const ATTR_NUM: u32 = OCI_ATTR_SQLFNCODE;
294}
295
296/// A type parameter for [`Statement::oci_attr`] to get [`OCI_ATTR_STATEMENT`],
297/// which is the text of the SQL statement prepared.
298///
299/// # Examples
300///
301/// ```
302/// # use oracle::Error;
303/// # use oracle::test_util;
304/// use oracle::oci_attr::Statement;
305/// # let mut conn = test_util::connect()?;
306///
307/// let mut stmt = conn.statement("select * from dual").build()?;
308/// assert_eq!(stmt.oci_attr::<Statement>()?, "select * from dual");
309/// # Ok::<(), Error>(())
310/// ```
311///
312/// [`Statement::oci_attr`]: crate::Statement::oci_attr
313/// [`OCI_ATTR_STATEMENT`]: https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-A251CF91-EB9F-4DBC-8BB8-FB5EA92C20DE__GUID-30B1693F-EFC7-4108-8F06-0EC1DC3785FB
314pub struct Statement;
315const OCI_ATTR_STATEMENT: u32 = 144;
316unsafe impl OciAttr for Statement {
317    type HandleType = Stmt;
318    type Mode = Read;
319    type DataType = str;
320    const ATTR_NUM: u32 = OCI_ATTR_STATEMENT;
321}
322
323#[cfg(test)]
324mod tests {
325    use super::*;
326    use crate::test_util;
327    use crate::Result;
328
329    struct StmtCacheSize;
330    unsafe impl OciAttr for StmtCacheSize {
331        type HandleType = SvcCtx;
332        type Mode = ReadWrite;
333        type DataType = u32;
334        const ATTR_NUM: u32 = 176;
335    }
336
337    struct InternalName;
338    unsafe impl OciAttr for InternalName {
339        type HandleType = Server;
340        type Mode = ReadWrite;
341        type DataType = str;
342        const ATTR_NUM: u32 = 25;
343    }
344
345    struct Module;
346    unsafe impl OciAttr for Module {
347        type HandleType = Session;
348        type Mode = Write;
349        type DataType = str;
350        const ATTR_NUM: u32 = 366;
351    }
352
353    #[test]
354    fn read_write_svcctx_u32_attr() -> Result<()> {
355        let mut conn = test_util::connect()?;
356        let size = conn.stmt_cache_size()?;
357        assert_eq!(conn.oci_attr::<StmtCacheSize>()?, size);
358        let new_size = size + 20;
359        conn.set_oci_attr::<StmtCacheSize>(&new_size)?;
360        assert_eq!(conn.oci_attr::<StmtCacheSize>()?, new_size);
361        Ok(())
362    }
363
364    #[test]
365    fn read_write_server_str_attr() -> Result<()> {
366        let mut conn = test_util::connect()?;
367        conn.set_oci_attr::<InternalName>("test internal name")?;
368        assert_eq!(conn.oci_attr::<InternalName>()?, "test internal name");
369        Ok(())
370    }
371
372    #[test]
373    fn write_session_str_attr() -> Result<()> {
374        let mut conn = test_util::connect()?;
375        conn.set_oci_attr::<Module>("test module name")?;
376        let module =
377            conn.query_row_as::<String>("select sys_context('USERENV', 'MODULE') from dual", &[])?;
378        assert_eq!(module, "test module name");
379        Ok(())
380    }
381}