oracle/
lib.rs

1// Rust-oracle - Rust binding for Oracle database
2//
3// URL: https://github.com/kubo/rust-oracle
4//
5//-----------------------------------------------------------------------------
6// Copyright (c) 2017-2025 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#![doc = include_str!("../README.md")]
17#![cfg_attr(docsrs, feature(doc_cfg))]
18
19use odpic_sys::*;
20use std::os::raw::c_char;
21use std::ptr;
22use std::result;
23use std::slice;
24
25#[cfg(feature = "aq_unstable")]
26#[cfg_attr(docsrs, doc(cfg(feature = "aq_unstable")))]
27pub mod aq;
28mod batch;
29#[allow(dead_code)]
30#[allow(non_camel_case_types)]
31#[allow(non_snake_case)]
32#[allow(improper_ctypes)]
33pub mod conn;
34mod connection;
35mod context;
36mod error;
37pub mod io;
38pub mod oci_attr;
39pub mod pool;
40#[cfg(doctest)]
41mod procmacro;
42mod row;
43pub mod sql_type;
44mod sql_value;
45mod statement;
46mod util;
47mod version;
48
49pub use crate::batch::Batch;
50pub use crate::batch::BatchBindIndex;
51pub use crate::batch::BatchBuilder;
52pub use crate::connection::ConnStatus;
53pub use crate::connection::Connection;
54pub use crate::connection::Connector;
55pub use crate::connection::Privilege;
56pub use crate::connection::ShutdownMode;
57pub use crate::connection::StartupMode;
58use crate::context::Context;
59pub use crate::context::InitParams;
60pub use crate::error::DbError;
61pub use crate::error::Error;
62pub use crate::error::ErrorKind;
63pub use crate::error::ParseOracleTypeError;
64pub use crate::row::ResultSet;
65pub use crate::row::Row;
66pub use crate::row::RowValue;
67pub use crate::sql_value::SqlValue;
68pub use crate::statement::BindIndex;
69pub use crate::statement::ColumnIndex;
70pub use crate::statement::ColumnInfo;
71pub use crate::statement::Statement;
72pub use crate::statement::StatementBuilder;
73pub use crate::statement::StatementType;
74pub use crate::version::Version;
75pub use oracle_procmacro::RowValue;
76
77pub type Result<T> = result::Result<T, Error>;
78
79macro_rules! define_dpi_data_with_refcount {
80    ($name:ident) => {
81        define_dpi_data_with_refcount!(__define_struct__, $name);
82        paste::item! {
83            unsafe impl Send for [<Dpi $name>] {}
84            unsafe impl Sync for [<Dpi $name>] {}
85        }
86    };
87
88    ($name:ident, nosync) => {
89        define_dpi_data_with_refcount!(__define_struct__, $name);
90        paste::item! {
91            unsafe impl Send for [<Dpi $name>] {}
92        }
93    };
94
95    (__define_struct__, $name:ident) => {
96        paste::item! {
97            #[derive(Debug)]
98            struct [<Dpi $name>] {
99                raw: *mut [<dpi $name>],
100            }
101
102            impl [<Dpi $name>] {
103                fn new(raw: *mut [<dpi $name>]) -> [<Dpi $name>] {
104                    [<Dpi $name>] { raw }
105                }
106
107                #[allow(dead_code)]
108                fn with_add_ref(raw: *mut [<dpi $name>]) -> [<Dpi $name>] {
109                    unsafe { [<dpi $name _addRef>](raw) };
110                    [<Dpi $name>] { raw }
111                }
112
113                #[allow(dead_code)]
114                fn null() -> [<Dpi $name>] {
115                    [<Dpi $name>] {
116                        raw: ptr::null_mut(),
117                    }
118                }
119
120                #[allow(dead_code)]
121                fn is_null(&self) -> bool {
122                    self.raw.is_null()
123                }
124
125                pub(crate) fn raw(&self) -> *mut [<dpi $name>] {
126                    self.raw
127                }
128            }
129
130            impl Clone for [<Dpi $name>] {
131                fn clone(&self) -> [<Dpi $name>] {
132                    if !self.is_null() {
133                        unsafe { [<dpi $name _addRef>](self.raw()) };
134                    }
135                    [<Dpi $name>]::new(self.raw())
136                }
137            }
138
139            impl Drop for [<Dpi $name>] {
140                fn drop(&mut self) {
141                   if !self.is_null() {
142                       unsafe { [<dpi $name _release>](self.raw()) };
143                   }
144                }
145            }
146        }
147    };
148}
149
150// define DpiConn wrapping *mut dpiConn.
151define_dpi_data_with_refcount!(Conn);
152
153// define DpiMsgProps wrapping *mut dpiMsgProps.
154define_dpi_data_with_refcount!(MsgProps);
155
156// define DpiObjectType wrapping *mut dpiObjectType.
157define_dpi_data_with_refcount!(ObjectType);
158
159// define DpiPool wrapping *mut dpiPool.
160define_dpi_data_with_refcount!(Pool);
161
162// define DpiObjectAttr wrapping *mut dpiObjectAttr.
163define_dpi_data_with_refcount!(ObjectAttr);
164
165// define DpiQueue wrapping *mut dpiQueue.
166define_dpi_data_with_refcount!(Queue);
167
168// define DpiObject wrapping *mut dpiObject.
169define_dpi_data_with_refcount!(Object, nosync);
170
171// define DpiStmt wrapping *mut dpiStmt.
172define_dpi_data_with_refcount!(Stmt, nosync);
173
174// define DpiVar wrapping *mut dpiVar.
175#[derive(Debug)]
176struct DpiVar {
177    raw: *mut dpiVar,
178    data: *mut dpiData,
179}
180
181impl DpiVar {
182    fn new(raw: *mut dpiVar, data: *mut dpiData) -> DpiVar {
183        DpiVar { raw, data }
184    }
185
186    fn with_add_ref(raw: *mut dpiVar, data: *mut dpiData) -> DpiVar {
187        unsafe { dpiVar_addRef(raw) };
188        DpiVar::new(raw, data)
189    }
190
191    fn is_null(&self) -> bool {
192        self.raw.is_null()
193    }
194}
195
196impl Drop for DpiVar {
197    fn drop(&mut self) {
198        if !self.is_null() {
199            unsafe { dpiVar_release(self.raw) };
200        }
201    }
202}
203
204unsafe impl Send for DpiVar {}
205
206#[allow(dead_code)]
207trait AssertSend: Send {}
208#[allow(dead_code)]
209trait AssertSync: Sync {}
210
211//
212// Utility struct to convert Rust strings from/to ODPI-C strings
213//
214
215struct OdpiStr {
216    pub ptr: *const c_char,
217    pub len: u32,
218}
219
220impl OdpiStr {
221    fn new<T>(s: T) -> OdpiStr
222    where
223        T: AsRef<[u8]>,
224    {
225        let s = s.as_ref();
226        if s.is_empty() {
227            OdpiStr {
228                ptr: ptr::null(),
229                len: 0,
230            }
231        } else {
232            OdpiStr {
233                ptr: s.as_ptr() as *const c_char,
234                len: s.len() as u32,
235            }
236        }
237    }
238
239    #[allow(clippy::inherent_to_string)]
240    pub fn to_string(&self) -> String {
241        to_rust_str(self.ptr, self.len)
242    }
243
244    #[cfg(feature = "aq_unstable")]
245    pub fn to_vec(&self) -> Vec<u8> {
246        if self.ptr.is_null() {
247            Vec::new()
248        } else {
249            let ptr = self.ptr as *mut u8;
250            let len = self.len as usize;
251            unsafe { Vec::from_raw_parts(ptr, len, len) }
252        }
253    }
254}
255
256fn to_rust_str(ptr: *const c_char, len: u32) -> String {
257    if ptr.is_null() {
258        "".to_string()
259    } else {
260        let s = unsafe { slice::from_raw_parts(ptr as *mut u8, len as usize) };
261        String::from_utf8_lossy(s).into_owned()
262    }
263}
264
265fn to_rust_slice<'a>(ptr: *const c_char, len: u32) -> &'a [u8] {
266    if ptr.is_null() {
267        &[]
268    } else {
269        unsafe { slice::from_raw_parts(ptr as *mut u8, len as usize) }
270    }
271}
272
273mod private {
274    use std::os::raw::c_void;
275
276    pub trait Sealed {}
277
278    impl Sealed for i8 {}
279    impl Sealed for u8 {}
280    impl Sealed for u16 {}
281    impl Sealed for u32 {}
282    impl Sealed for u64 {}
283    impl Sealed for usize {}
284    impl Sealed for f32 {}
285    impl Sealed for f64 {}
286    impl Sealed for bool {}
287    impl Sealed for str {}
288    impl Sealed for [u8] {}
289    impl Sealed for *mut c_void {}
290    impl Sealed for &str {}
291}
292
293#[allow(dead_code)]
294#[doc(hidden)]
295// #[cfg(doctest)] isn't usable here. See: https://github.com/rust-lang/rust/issues/67295
296pub mod test_util {
297    use super::*;
298    use std::env;
299
300    pub const VER11_2: Version = Version::new(11, 2, 0, 0, 0);
301    pub const VER12_1: Version = Version::new(12, 1, 0, 0, 0);
302    pub const VER18: Version = Version::new(18, 0, 0, 0, 0);
303    pub const VER23: Version = Version::new(23, 0, 0, 0, 0);
304    pub const VER23_5: Version = Version::new(23, 5, 0, 0, 0);
305
306    fn env_var_or(env_name: &str, default: &str) -> String {
307        match env::var_os(env_name) {
308            Some(env_var) => env_var.into_string().unwrap(),
309            None => String::from(default),
310        }
311    }
312
313    pub fn main_user() -> String {
314        env_var_or("ODPIC_TEST_MAIN_USER", "odpic")
315    }
316
317    pub fn main_password() -> String {
318        env_var_or("ODPIC_TEST_MAIN_PASSWORD", "welcome")
319    }
320
321    pub fn edition_user() -> String {
322        env_var_or("ODPIC_TEST_EDITION_USER", "odpic_edition")
323    }
324
325    pub fn edition_password() -> String {
326        env_var_or("ODPIC_TEST_EDITION_PASSWORD", "welcome")
327    }
328
329    pub fn connect_string() -> String {
330        env_var_or("ODPIC_TEST_CONNECT_STRING", "localhost/orclpdb")
331    }
332
333    pub fn connect() -> Result<Connection> {
334        Connection::connect(main_user(), main_password(), connect_string())
335    }
336
337    pub fn check_version(
338        conn: &Connection,
339        client_ver: &Version,
340        server_ver: &Version,
341    ) -> Result<bool> {
342        Ok(&Version::client()? >= client_ver && &conn.server_version()?.0 >= server_ver)
343    }
344}