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