oracle/sql_type/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-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//! SQL data types
17
18#[cfg(doc)]
19use crate::sql_type::vector::VecRef;
20#[cfg(doc)]
21use crate::sql_type::vector::Vector;
22use crate::Connection;
23use crate::ErrorKind;
24use crate::Result;
25use crate::SqlValue;
26
27#[cfg(feature = "chrono")]
28#[cfg_attr(docsrs, doc(cfg(feature = "chrono")))]
29mod chrono;
30pub mod collection;
31mod interval_ds;
32mod interval_ym;
33mod lob;
34mod object;
35mod oracle_type;
36mod ref_cursor;
37mod timestamp;
38pub mod vector;
39
40pub use self::interval_ds::IntervalDS;
41pub use self::interval_ym::IntervalYM;
42pub(crate) use self::lob::Bfile; // TODO: remove `(crate)`
43pub use self::lob::Blob;
44pub use self::lob::Clob;
45pub use self::lob::Lob;
46pub use self::lob::Nclob;
47pub use self::object::Collection;
48pub use self::object::Object;
49pub use self::object::ObjectType;
50pub use self::object::ObjectTypeAttr;
51pub(crate) use self::object::ObjectTypeInternal;
52pub use self::oracle_type::InnerValue;
53pub(crate) use self::oracle_type::NativeType;
54pub use self::oracle_type::OracleType;
55pub use self::ref_cursor::RefCursor;
56pub use self::timestamp::Timestamp;
57
58/// Conversion from Oracle values to rust values.
59///
60/// Values in Oracle are converted to Rust type as possible as it can.
61/// The following table indicates supported conversions.
62///
63/// | Oracle Type | Rust Type |
64/// | --- | --- |
65/// | character data types | String |
66/// | " | `i8`, `i16`, `i32`, `i64`, `isize`, `u8`, `u16`, `u32`, `u64`, `usize`, `f64`, `f32` by using ``String::parse`` |
67/// | " | `Vec<u8>` (The Oracle value must be in hexadecimal.) |
68/// | " | [`Timestamp`] by `String.parse()` |
69/// | " | [`IntervalDS`] by `String.parse()` |
70/// | " | [`IntervalYM`] by `String.parse()` |
71/// | numeric data types | `i8`, `i16`, `i32`, `i64`, `isize`, `u8`, `u16`, `u32`, `u64`, `usize`, `f64`, `f32` |
72/// | " | `String` |
73/// | `raw` | `Vec<u8>` |
74/// | " | `String` (The Oracle value is converted to characters in hexadecimal.) |
75/// | timestamp data types | [`Timestamp`] |
76/// | " | `String` |
77/// | `interval day to second` | [`IntervalDS`] |
78/// | " | [`std::time::Duration`] (conversion error for negative durations) |
79/// | " | `String` |
80/// | `interval year to month` | [`IntervalYM`] |
81/// | " | `String` |
82/// | [Oracle object] except [Oracle collection] | [`Object`] |
83/// | " | `String` |
84/// | [Oracle collection] | [`Collection`] |
85/// | " | `String` |
86/// | `rowid` | `String` |
87/// | `ref cursor` | [`RefCursor`] |
88/// | `boolean` (PL/SQL only) | `bool` (Oracle client version >= 12.1) |
89/// | `vector(float32)` | `Vec<f32>` |
90/// | `vector(float64)` | `Vec<f64>` |
91/// | `vector(int8)` | `Vec<i8>` |
92/// | `vector(binary)` | `Vec<u8>` |
93/// | `vector(*)` | [`Vector`] |
94///
95/// When `chrono` feature is enabled, the following conversions are added.
96///
97/// | Oracle Type | Rust Type |
98/// | --- | --- |
99/// | timestamp data types | [`chrono::DateTime`] |
100/// | " | [`chrono::Date`] |
101/// | " | [`chrono::naive::NaiveDateTime`] |
102/// | " | [`chrono::naive::NaiveDate`] |
103/// | interval day to second | [`chrono::Duration`], which is alias of [`chrono::TimeDelta`] since chrono 0.4.43 |
104///
105/// This conversion is used also to get values from output parameters.
106///
107/// [Oracle object]: https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-8F0BA083-FA6D-4373-B440-50FDDA4D6E90
108/// [Oracle collection]: https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-1200DD46-95C0-4776-90BB-0ED0CD61267E
109/// [`chrono::Date`]: https://docs.rs/chrono/0.4/chrono/struct.Date.html
110/// [`chrono::DateTime`]: https://docs.rs/chrono/0.4/chrono/struct.DateTime.html
111/// [`chrono::naive::NaiveDate`]: https://docs.rs/chrono/0.4/chrono/naive/struct.NaiveDate.html
112/// [`chrono::naive::NaiveDateTime`]: https://docs.rs/chrono/0.4/chrono/naive/struct.NaiveDateTime.html
113/// [`chrono::Duration`]: https://docs.rs/chrono/0.4/chrono/type.Duration.html
114/// [`chrono::TimeDelta`]: https://docs.rs/chrono/0.4/chrono/struct.TimeDelta.html
115pub trait FromSql {
116 fn from_sql(val: &SqlValue) -> Result<Self>
117 where
118 Self: Sized;
119}
120
121/// A trait specifying Oracle type to bind a null value.
122///
123/// This trait is used only when binding a `None` value of `Option<T>`.
124/// The type of the null value is determined by the rust type.
125///
126/// | Rust Type | Oracle Type |
127/// | --- | --- |
128/// | `str`, `String` | `nvarchar2(0)` |
129/// | `i8`, `i16`, `i32`, `i64`, `u8`, `u16`, `u32`, `u64`, `f32`, `f64` | `number` |
130/// | `Vec<u8>` | `raw(0)` |
131/// | `bool` | `boolean` (PL/SQL only) |
132/// | [`Timestamp`] | `timestamp(9) with time zone` |
133/// | [`IntervalDS`] | `interval day(9) to second(9)` |
134/// | [`IntervalYM`] | `interval year(9) to month` |
135/// | [`RefCursor`] | `ref cursor` |
136/// | [`VecRef`] | `vector` |
137///
138/// When `chrono` feature is enabled, the followings are added.
139///
140/// | Rust Type | Oracle Type |
141/// | --- | --- |
142/// | [`chrono::Date`] | `timestamp(0) with time zone` |
143/// | [`chrono::DateTime`] | `timestamp(9) with time zone` |
144/// | [`chrono::naive::NaiveDate`] | `timestamp(0)` |
145/// | [`chrono::naive::NaiveDateTime`] | `timestamp(9)` |
146/// | [`chrono::Duration`], which is alias of [`chrono::TimeDelta`] since chrono 0.4.43 ] | `interval day(9) to second(9)` |
147///
148/// [`chrono::Date`]: https://docs.rs/chrono/0.4/chrono/struct.Date.html
149/// [`chrono::DateTime`]: https://docs.rs/chrono/0.4/chrono/struct.DateTime.html
150/// [`chrono::naive::NaiveDate`]: https://docs.rs/chrono/0.4/chrono/naive/struct.NaiveDate.html
151/// [`chrono::naive::NaiveDateTime`]: https://docs.rs/chrono/0.4/chrono/naive/struct.NaiveDateTime.html
152/// [`chrono::Duration`]: https://docs.rs/chrono/0.4/chrono/type.Duration.html
153/// [`chrono::TimeDelta`]: https://docs.rs/chrono/0.4/chrono/struct.TimeDelta.html
154pub trait ToSqlNull {
155 fn oratype_for_null(conn: &Connection) -> Result<OracleType>;
156}
157
158/// Conversion from rust values to Oracle values.
159///
160/// The type of the Oracle value is determined by the rust type.
161///
162/// | Rust Type | Oracle Type | Oracle Value |
163/// | --- | --- | --- |
164/// | `str`, `String` | `nvarchar2(length of the rust value)` | The specified value |
165/// | `i8`, `i16`, `i32`, `i64`, `isize`, `u8`, `u16`, `u32`, `u64`, `usize`, `f32`, `f64` | `number` | The specified value |
166/// | `Vec<u8>` | `raw(length of the rust value)` | The specified value |
167/// | `bool` | `boolean` (PL/SQL only) | The specified value |
168/// | [`Timestamp`] | `timestamp(9) with time zone` | The specified value |
169/// | [`IntervalDS`] | `interval day(9) to second(9)` | The specified value |
170/// | [`IntervalYM`] | `interval year(9) to month` | The specified value |
171/// | [`Collection`] | type returned by [`Collection::object_type`] | The specified value |
172/// | [`Object`] | type returned by [`Object::object_type`] | The specified value |
173/// | [`VecRef`] | `vector` |
174/// | `Option<T>` where T: `ToSql` + [`ToSqlNull`] | When the value is `Some`, the contained value decides the Oracle type. When it is `None`, ToSqlNull decides it. | When the value is `Some`, the contained value. When it is `None`, a null value.
175/// | [`OracleType`] | type represented by the OracleType. | a null value |
176/// | `(&ToSql, &OracleType)` | type represented by the second element. | The value of the first element |
177///
178/// When you need to bind output parameters such as varchar2, use `OracleType`
179/// or `(&ToSql, &OracleType)` to specify the maximum length of data types.
180///
181/// When `chrono` feature is enabled, the following conversions are added.
182///
183/// | Rust Type | Oracle Type |
184/// | --- | --- |
185/// | [`chrono::Date`] | `timestamp(0) with time zone` |
186/// | [`chrono::DateTime`] | `timestamp(9) with time zone` |
187/// | [`chrono::naive::NaiveDate`] | `timestamp(0)` |
188/// | [`chrono::naive::NaiveDateTime`] | `timestamp(9)` |
189/// | [`chrono::Duration`], which is alias of [`chrono::TimeDelta`] since chrono 0.4.43 ] | `interval day(9) to second(9)` |
190///
191/// [`chrono::Date`]: https://docs.rs/chrono/0.4/chrono/struct.Date.html
192/// [`chrono::DateTime`]: https://docs.rs/chrono/0.4/chrono/struct.DateTime.html
193/// [`chrono::naive::NaiveDate`]: https://docs.rs/chrono/0.4/chrono/naive/struct.NaiveDate.html
194/// [`chrono::naive::NaiveDateTime`]: https://docs.rs/chrono/0.4/chrono/naive/struct.NaiveDateTime.html
195/// [`chrono::Duration`]: https://docs.rs/chrono/0.4/chrono/type.Duration.html
196/// [`chrono::TimeDelta`]: https://docs.rs/chrono/0.4/chrono/struct.TimeDelta.html
197///
198pub trait ToSql {
199 fn oratype(&self, conn: &Connection) -> Result<OracleType>;
200 fn to_sql(&self, val: &mut SqlValue) -> Result<()>;
201}
202
203macro_rules! impl_from_sql {
204 ($type:ty, $func:ident) => {
205 impl FromSql for $type {
206 fn from_sql(val: &SqlValue) -> Result<$type> {
207 val.$func()
208 }
209 }
210 };
211}
212
213macro_rules! impl_to_sql {
214 ($type:ty, $func:ident, $oratype:expr) => {
215 impl ToSqlNull for $type {
216 fn oratype_for_null(_conn: &Connection) -> Result<OracleType> {
217 Ok($oratype)
218 }
219 }
220 impl ToSql for $type {
221 fn oratype(&self, _conn: &Connection) -> Result<OracleType> {
222 Ok($oratype)
223 }
224 fn to_sql(&self, val: &mut SqlValue) -> Result<()> {
225 val.$func(self)
226 }
227 }
228 };
229}
230
231macro_rules! impl_from_and_to_sql {
232 ($type:ty, $to_func:ident, $set_func:ident, $oratype:expr) => {
233 impl_from_sql!($type, $to_func);
234 impl_to_sql!($type, $set_func, $oratype);
235 };
236 ($to_type:ty, $to_func:ident, $set_type:ty, $set_func:ident, $oratype:expr) => {
237 impl_from_sql!($to_type, $to_func);
238 impl_to_sql!($set_type, $set_func, $oratype);
239 };
240}
241
242impl_from_and_to_sql!(i8, to_i8, set_i8, OracleType::Number(0, 0));
243impl_from_and_to_sql!(i16, to_i16, set_i16, OracleType::Number(0, 0));
244impl_from_and_to_sql!(i32, to_i32, set_i32, OracleType::Number(0, 0));
245impl_from_and_to_sql!(i64, to_i64, set_i64, OracleType::Number(0, 0));
246impl_from_and_to_sql!(isize, to_isize, set_isize, OracleType::Number(0, 0));
247impl_from_and_to_sql!(u8, to_u8, set_u8, OracleType::Number(0, 0));
248impl_from_and_to_sql!(u16, to_u16, set_u16, OracleType::Number(0, 0));
249impl_from_and_to_sql!(u32, to_u32, set_u32, OracleType::Number(0, 0));
250impl_from_and_to_sql!(u64, to_u64, set_u64, OracleType::Number(0, 0));
251impl_from_and_to_sql!(usize, to_usize, set_usize, OracleType::Number(0, 0));
252impl_from_and_to_sql!(f64, to_f64, set_f64, OracleType::Number(0, 0));
253impl_from_and_to_sql!(f32, to_f32, set_f32, OracleType::Number(0, 0));
254impl_from_and_to_sql!(bool, to_bool, set_bool, OracleType::Boolean);
255impl_from_sql!(String, to_string);
256impl_from_sql!(Vec<u8>, to_bytes);
257impl_from_sql!(Vec<f32>, to_f32_vec);
258impl_from_sql!(Vec<f64>, to_f64_vec);
259impl_from_sql!(Vec<i8>, to_i8_vec);
260impl_from_and_to_sql!(
261 Timestamp,
262 to_timestamp,
263 Timestamp,
264 set_timestamp,
265 OracleType::TimestampTZ(9)
266);
267impl_from_and_to_sql!(
268 IntervalDS,
269 to_interval_ds,
270 IntervalDS,
271 set_interval_ds,
272 OracleType::IntervalDS(9, 9)
273);
274impl_from_and_to_sql!(
275 IntervalYM,
276 to_interval_ym,
277 IntervalYM,
278 set_interval_ym,
279 OracleType::IntervalYM(9)
280);
281
282impl ToSqlNull for String {
283 fn oratype_for_null(_conn: &Connection) -> Result<OracleType> {
284 Ok(OracleType::NVarchar2(0))
285 }
286}
287
288impl ToSql for String {
289 fn oratype(&self, _conn: &Connection) -> Result<OracleType> {
290 Ok(OracleType::NVarchar2(self.len() as u32))
291 }
292 fn to_sql(&self, val: &mut SqlValue) -> Result<()> {
293 val.set_string(self)
294 }
295}
296
297impl ToSqlNull for Vec<u8> {
298 fn oratype_for_null(_conn: &Connection) -> Result<OracleType> {
299 Ok(OracleType::Raw(0))
300 }
301}
302
303impl ToSql for Vec<u8> {
304 fn oratype(&self, _conn: &Connection) -> Result<OracleType> {
305 Ok(OracleType::Raw(self.len() as u32))
306 }
307 fn to_sql(&self, val: &mut SqlValue) -> Result<()> {
308 val.set_bytes(self)
309 }
310}
311
312impl ToSqlNull for &str {
313 fn oratype_for_null(_conn: &Connection) -> Result<OracleType> {
314 Ok(OracleType::NVarchar2(0))
315 }
316}
317
318impl ToSql for &str {
319 fn oratype(&self, _conn: &Connection) -> Result<OracleType> {
320 Ok(OracleType::NVarchar2(self.len() as u32))
321 }
322 fn to_sql(&self, val: &mut SqlValue) -> Result<()> {
323 val.set_string(self)
324 }
325}
326
327impl ToSqlNull for &[u8] {
328 fn oratype_for_null(_conn: &Connection) -> Result<OracleType> {
329 Ok(OracleType::Raw(0))
330 }
331}
332
333impl ToSql for &[u8] {
334 fn oratype(&self, _conn: &Connection) -> Result<OracleType> {
335 Ok(OracleType::Raw(self.len() as u32))
336 }
337 fn to_sql(&self, val: &mut SqlValue) -> Result<()> {
338 val.set_bytes(self)
339 }
340}
341
342impl<const N: usize> ToSql for &[u8; N] {
343 fn oratype(&self, _conn: &Connection) -> Result<OracleType> {
344 Ok(OracleType::Raw(self.len() as u32))
345 }
346 fn to_sql(&self, val: &mut SqlValue) -> Result<()> {
347 // Use self.as_slice() instead of &self[..] when MSRV become 1.57 or later.
348 val.set_bytes(&self[..])
349 }
350}
351
352impl<T: FromSql> FromSql for Option<T> {
353 fn from_sql(val: &SqlValue) -> Result<Option<T>> {
354 match <T>::from_sql(val) {
355 Ok(v) => Ok(Some(v)),
356 Err(err) if err.kind() == ErrorKind::NullValue => Ok(None),
357 Err(err) => Err(err),
358 }
359 }
360}
361
362impl<T: ToSql + ToSqlNull> ToSql for Option<T> {
363 fn oratype(&self, conn: &Connection) -> Result<OracleType> {
364 match *self {
365 Some(ref t) => t.oratype(conn),
366 None => <T>::oratype_for_null(conn),
367 }
368 }
369 fn to_sql(&self, val: &mut SqlValue) -> Result<()> {
370 match *self {
371 Some(ref t) => t.to_sql(val),
372 None => val.set_null(),
373 }
374 }
375}
376
377impl ToSql for OracleType {
378 fn oratype(&self, _conn: &Connection) -> Result<OracleType> {
379 Ok(self.clone())
380 }
381 fn to_sql(&self, val: &mut SqlValue) -> Result<()> {
382 val.set_null()?;
383 Ok(())
384 }
385}
386
387impl<T: ToSql> ToSql for (&T, &OracleType) {
388 fn oratype(&self, _conn: &Connection) -> Result<OracleType> {
389 Ok(self.1.clone())
390 }
391 fn to_sql(&self, val: &mut SqlValue) -> Result<()> {
392 (*self.0).to_sql(val)
393 }
394}