oracle/sql_type/
object.rs

1// Rust-oracle - Rust binding for Oracle database
2//
3// URL: https://github.com/kubo/rust-oracle
4//
5//-----------------------------------------------------------------------------
6// Copyright (c) 2017-2018 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::connection::Conn;
18use crate::sql_type::collection::{Indices, Iter, Values};
19use crate::sql_type::FromSql;
20use crate::sql_type::OracleType;
21use crate::sql_type::ToSql;
22use crate::to_rust_str;
23use crate::util::write_literal;
24use crate::AssertSend;
25use crate::Connection;
26use crate::Context;
27use crate::DpiObject;
28use crate::DpiObjectAttr;
29use crate::DpiObjectType;
30use crate::Error;
31use crate::Result;
32use crate::SqlValue;
33use odpic_sys::dpi_impl::DPI_NUMBER_AS_TEXT_CHARS;
34use odpic_sys::*;
35use std::cmp;
36use std::fmt;
37use std::mem::{self, MaybeUninit};
38use std::os::raw::c_char;
39use std::sync::Arc;
40
41unsafe fn release_dpi_data(data: &dpiData, native_type_num: u32) {
42    if data.isNull == 0 {
43        match native_type_num {
44            DPI_NATIVE_TYPE_LOB => {
45                dpiLob_release(data.value.asLOB);
46            }
47            DPI_NATIVE_TYPE_OBJECT => {
48                dpiObject_release(data.value.asObject);
49            }
50            DPI_NATIVE_TYPE_ROWID => {
51                dpiRowid_release(data.value.asRowid);
52            }
53            _ => (),
54        }
55    }
56}
57
58/// Oracle-specific collection data type
59///
60/// This type corresponds to varray and nested table data types.
61/// See [Oracle manual](https://docs.oracle.com/database/122/ADOBJ/collection-data-types.htm).
62///
63/// ```no_run
64/// # use oracle::*;
65/// let conn = Connection::connect("scott", "tiger", "")?;
66///
67/// // MDSYS.SDO_ELEM_INFO_ARRAY is defined as VARRAY (1048576) of NUMBER.
68/// let objtype = conn.object_type("MDSYS.SDO_ELEM_INFO_ARRAY")?;
69///
70/// // Create a new collection
71/// let mut obj = objtype.new_collection()?;
72/// obj.push(&1);
73/// obj.push(&3);
74/// assert_eq!(obj.to_string(), "MDSYS.SDO_ELEM_INFO_ARRAY(1, 3)");
75/// # Ok::<(), Error>(())
76/// ```
77///
78/// Note: Methods in the type may be changed in future.
79pub struct Collection {
80    conn: Conn,
81    pub(crate) handle: DpiObject,
82    objtype: ObjectType,
83}
84
85impl Collection {
86    pub(crate) fn new(conn: Conn, handle: DpiObject, objtype: ObjectType) -> Collection {
87        Collection {
88            conn,
89            handle,
90            objtype,
91        }
92    }
93
94    pub(crate) fn ctxt(&self) -> &Context {
95        self.conn.ctxt()
96    }
97
98    fn handle(&self) -> *mut dpiObject {
99        self.handle.raw
100    }
101
102    /// Returns type information.
103    pub fn object_type(&self) -> &ObjectType {
104        &self.objtype
105    }
106
107    /// Returns the number of elements.
108    ///
109    /// This counts also deleted elements. See "Comments" about [OCICollSize()][].
110    ///
111    /// [OCICollSize()]: https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-B8F6665F-12F1-43DB-A27E-82A2A655D701
112    pub fn size(&self) -> Result<i32> {
113        let mut size = 0;
114        chkerr!(self.ctxt(), dpiObject_getSize(self.handle(), &mut size));
115        Ok(size)
116    }
117
118    /// Returns an iterator visiting all values with indices in the collection.
119    ///
120    /// ```
121    /// # use oracle::{Error, Result};
122    /// # use oracle::test_util;
123    /// # let conn = test_util::connect()?;
124    /// // Creates VARRAY type and gets the type information
125    /// conn.execute("create or replace type string_varray is varray(20) of varchar2(60)", &[])?;
126    /// let objtype = conn.object_type("STRING_VARRAY")?;
127    ///
128    /// // Creates a VARRAY instance and appends three elements.
129    /// let mut coll = objtype.new_collection()?;
130    /// coll.push(&"First Element")?;
131    /// coll.push(&"Second Element")?;
132    /// coll.push(&"Third Element")?;
133    ///
134    /// let vec = coll
135    ///     .iter::<String>() // iterator returning Result<(i32, String)>
136    ///     .collect::<Result<Vec<_>>>()?;
137    /// assert_eq!(vec[0], (0, "First Element".to_string()));
138    /// assert_eq!(vec[1], (1, "Second Element".to_string()));
139    /// assert_eq!(vec[2], (2, "Third Element".to_string()));
140    ///
141    /// // Creates Table type and gets the type information
142    /// conn.execute("create or replace type string_table is table of varchar2(60)", &[])?;
143    /// let objtype = conn.object_type("STRING_TABLE")?;
144    ///
145    /// // Creates a TABLE instance, appends four elements and makes a hole.
146    /// let mut coll = objtype.new_collection()?;
147    /// coll.push(&"First Element")?;
148    /// coll.push(&"Second Element")?;
149    /// coll.push(&"Third Element")?;
150    /// coll.push(&"Fourth Element")?;
151    /// coll.remove(2)?; // Remove "Third Element"
152    ///
153    /// // iterator returning Result<(i32, String)>
154    /// let mut iter = coll.iter::<String>();
155    /// assert_eq!(iter.next().unwrap()?, (0, "First Element".to_string()));
156    /// assert_eq!(iter.next().unwrap()?, (1, "Second Element".to_string()));
157    /// assert_eq!(iter.next().unwrap()?, (3, "Fourth Element".to_string()));
158    /// assert!(iter.next().is_none());
159    /// # // check fused
160    /// # assert!(iter.next().is_none());
161    /// # // backward
162    /// # assert_eq!(iter.next_back().unwrap()?, (3, "Fourth Element".to_string()));
163    /// # assert_eq!(iter.next_back().unwrap()?, (1, "Second Element".to_string()));
164    /// # assert_eq!(iter.next_back().unwrap()?, (0, "First Element".to_string()));
165    /// # assert!(iter.next_back().is_none());
166    /// # // check fused
167    /// # assert!(iter.next_back().is_none());
168    ///
169    /// # Ok::<(), Box<dyn std::error::Error>>(())
170    /// ```
171    pub fn iter<T>(&self) -> Iter<T>
172    where
173        T: FromSql,
174    {
175        Iter::new(self)
176    }
177
178    /// Returns an iterator visiting all indices in the collection.
179    ///
180    /// ```
181    /// # use oracle::{Error, Result};
182    /// # use oracle::test_util;
183    /// # let conn = test_util::connect()?;
184    /// // Creates Table type and gets the type information
185    /// conn.execute("create or replace type string_table is table of varchar2(60)", &[])?;
186    /// let objtype = conn.object_type("STRING_TABLE")?;
187    ///
188    /// // Creates a TABLE instance, appends four elements and makes a hole.
189    /// let mut coll = objtype.new_collection()?;
190    /// coll.push(&"First Element")?; // index 0
191    /// coll.push(&"Second Element")?; // index 1
192    /// coll.push(&"Third Element")?; // index 2
193    /// coll.push(&"Fourth Element")?; // index 3
194    /// coll.remove(2)?; // remote index 2
195    /// // coll's indices are 0, 1 and 3.
196    ///
197    /// let mut indices = coll.indices();
198    /// assert_eq!(indices.next().unwrap().unwrap(), 0);
199    /// assert_eq!(indices.next().unwrap().unwrap(), 1);
200    /// assert_eq!(indices.next().unwrap().unwrap(), 3);
201    /// assert!(indices.next().is_none());
202    /// # // check fused or not
203    /// # assert!(indices.next().is_none());
204    /// # // backward
205    /// # assert_eq!(indices.next_back().unwrap()?, 3);
206    /// # assert_eq!(indices.next_back().unwrap()?, 1);
207    /// # assert_eq!(indices.next_back().unwrap()?, 0);
208    /// # assert!(indices.next_back().is_none());
209    /// # // check fused or not
210    /// # assert!(indices.next_back().is_none());
211    /// # Ok::<(), Box<dyn std::error::Error>>(())
212    /// ```
213    pub fn indices(&self) -> Indices {
214        Indices::new(self)
215    }
216
217    /// Returns an iterator visiting all values in the collection.
218    ///
219    /// ```
220    /// # use oracle::{Error, Result};
221    /// # use oracle::test_util;
222    /// # let conn = test_util::connect()?;
223    /// // Creates VARRAY type and gets the type information
224    /// conn.execute("create or replace type string_varray3 is varray(20) of varchar2(60)", &[])?;
225    /// let objtype = conn.object_type("STRING_VARRAY3")?;
226    ///
227    /// // Creates a VARRAY instance and appends three elements.
228    /// let mut coll = objtype.new_collection()?;
229    /// coll.push(&"First Element");
230    /// coll.push(&"Second Element");
231    /// coll.push(&"Third Element");
232    ///
233    /// let mut iter = coll.values::<String>();
234    /// assert_eq!(iter.next().unwrap()?, "First Element".to_string());
235    /// assert_eq!(iter.next().unwrap()?, "Second Element".to_string());
236    /// assert_eq!(iter.next().unwrap()?, "Third Element".to_string());
237    /// assert!(iter.next().is_none());
238    /// # // check fused
239    /// # assert!(iter.next().is_none());
240    /// # // backward
241    /// # assert_eq!(iter.next_back().unwrap()?, "Third Element".to_string());
242    /// # assert_eq!(iter.next_back().unwrap()?, "Second Element".to_string());
243    /// # assert_eq!(iter.next_back().unwrap()?, "First Element".to_string());
244    /// # assert!(iter.next_back().is_none());
245    /// # // check fused
246    /// # assert!(iter.next_back().is_none());
247    ///
248    /// # Ok::<(), Box<dyn std::error::Error>>(())
249    /// ```
250    pub fn values<T>(&self) -> Values<T>
251    where
252        T: FromSql,
253    {
254        Values::new(self)
255    }
256
257    /// Returns the first index.
258    ///
259    /// Use this method if indexes of the collection isn't continuous.
260    pub fn first_index(&self) -> Result<i32> {
261        let mut index = 0;
262        let mut exists = 0;
263        chkerr!(
264            self.ctxt(),
265            dpiObject_getFirstIndex(self.handle(), &mut index, &mut exists)
266        );
267        if exists != 0 {
268            Ok(index)
269        } else {
270            Err(Error::no_data_found())
271        }
272    }
273
274    /// Returns the last index.
275    ///
276    /// Use this method if indexes of the collection isn't continuous.
277    pub fn last_index(&self) -> Result<i32> {
278        let mut index = 0;
279        let mut exists = 0;
280        chkerr!(
281            self.ctxt(),
282            dpiObject_getLastIndex(self.handle(), &mut index, &mut exists)
283        );
284        if exists != 0 {
285            Ok(index)
286        } else {
287            Err(Error::no_data_found())
288        }
289    }
290
291    /// Returns the next index following the specified index.
292    ///
293    /// Use this method if indexes of the collection isn't continuous.
294    pub fn next_index(&self, index: i32) -> Result<i32> {
295        let mut next = 0;
296        let mut exists = 0;
297        chkerr!(
298            self.ctxt(),
299            dpiObject_getNextIndex(self.handle(), index, &mut next, &mut exists)
300        );
301        if exists != 0 {
302            Ok(next)
303        } else {
304            Err(Error::no_data_found())
305        }
306    }
307
308    /// Returns the previous index following the specified index.
309    ///
310    /// Use this method if indexes of the collection isn't continuous.
311    pub fn prev_index(&self, index: i32) -> Result<i32> {
312        let mut prev = 0;
313        let mut exists = 0;
314        chkerr!(
315            self.ctxt(),
316            dpiObject_getPrevIndex(self.handle(), index, &mut prev, &mut exists)
317        );
318        if exists != 0 {
319            Ok(prev)
320        } else {
321            Err(Error::no_data_found())
322        }
323    }
324
325    /// Returns whether an element exists at the specified index.
326    pub fn exist(&self, index: i32) -> Result<bool> {
327        let mut exists = 0;
328        chkerr!(
329            self.ctxt(),
330            dpiObject_getElementExistsByIndex(self.handle(), index, &mut exists)
331        );
332        Ok(exists != 0)
333    }
334
335    /// Returns the value of the element at the specified index.
336    pub fn get<T>(&self, index: i32) -> Result<T>
337    where
338        T: FromSql,
339    {
340        let oratype = self.objtype.element_oracle_type().unwrap();
341        let mut data = unsafe { mem::zeroed() };
342        let mut buf = [0 as c_char; DPI_NUMBER_AS_TEXT_CHARS as usize];
343        match oratype {
344            &OracleType::Number(_, _) | &OracleType::Float(_) => unsafe {
345                dpiData_setBytes(&mut data, buf.as_mut_ptr(), buf.len() as u32);
346            },
347            _ => (),
348        }
349        let res;
350        let native_type_num;
351        {
352            let sql_value = SqlValue::from_oratype(self.conn.clone(), oratype, &mut data)?;
353            native_type_num = sql_value.native_type_num();
354            chkerr!(
355                self.ctxt(),
356                dpiObject_getElementValueByIndex(
357                    self.handle(),
358                    index,
359                    native_type_num,
360                    sql_value.data()?
361                )
362            );
363            res = sql_value.get();
364        }
365        unsafe { release_dpi_data(&data, native_type_num) };
366        res
367    }
368
369    /// Sets the value to the element at the specified index.
370    pub fn set(&mut self, index: i32, value: &dyn ToSql) -> Result<()> {
371        let oratype = self.objtype.element_oracle_type().unwrap();
372        let mut data = unsafe { mem::zeroed() };
373        let mut sql_value = SqlValue::from_oratype(self.conn.clone(), oratype, &mut data)?;
374        sql_value.set(value)?;
375        chkerr!(
376            self.ctxt(),
377            dpiObject_setElementValueByIndex(
378                self.handle(),
379                index,
380                sql_value.native_type_num(),
381                sql_value.data()?
382            )
383        );
384        Ok(())
385    }
386
387    /// Appends an element to the end of the collection.
388    pub fn push(&mut self, value: &dyn ToSql) -> Result<()> {
389        let oratype = self.objtype.element_oracle_type().unwrap();
390        let mut data = unsafe { mem::zeroed() };
391        let mut sql_value = SqlValue::from_oratype(self.conn.clone(), oratype, &mut data)?;
392        sql_value.set(value)?;
393        chkerr!(
394            self.ctxt(),
395            dpiObject_appendElement(
396                self.handle(),
397                sql_value.native_type_num(),
398                sql_value.data()?
399            )
400        );
401        Ok(())
402    }
403
404    /// Remove the element at the specified index.
405    /// Note that the position ordinals of the remaining elements are not changed.
406    /// The operation creates **holes** in the collection.
407    pub fn remove(&mut self, index: i32) -> Result<()> {
408        chkerr!(
409            self.ctxt(),
410            dpiObject_deleteElementByIndex(self.handle(), index)
411        );
412        Ok(())
413    }
414
415    /// Trims a number of elements from the end of a collection.
416    ///
417    /// If the number of of elements to trim exceeds the current size
418    /// of the collection an error is returned.
419    pub fn trim(&mut self, len: usize) -> Result<()> {
420        chkerr!(self.ctxt(), dpiObject_trim(self.handle(), len as u32));
421        Ok(())
422    }
423}
424
425impl Clone for Collection {
426    fn clone(&self) -> Collection {
427        Collection::new(self.conn.clone(), self.handle.clone(), self.objtype.clone())
428    }
429}
430
431impl FromSql for Collection {
432    fn from_sql(val: &SqlValue) -> Result<Collection> {
433        val.to_collection()
434    }
435}
436
437impl ToSql for Collection {
438    fn oratype(&self, _conn: &Connection) -> Result<OracleType> {
439        Ok(OracleType::Object(self.object_type().clone()))
440    }
441    fn to_sql(&self, val: &mut SqlValue) -> Result<()> {
442        val.set_collection(self)
443    }
444}
445
446impl fmt::Display for Collection {
447    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
448        write!(f, "{}(", self.objtype)?;
449        if let Ok(index) = self.first_index() {
450            let mut idx = index;
451            let oratype = self.objtype.element_oracle_type().unwrap();
452            loop {
453                write_literal(f, &self.get(idx), oratype)?;
454                if let Ok(index) = self.next_index(idx) {
455                    idx = index;
456                    write!(f, ", ")?;
457                } else {
458                    break;
459                }
460            }
461        }
462        write!(f, ")")
463    }
464}
465
466impl fmt::Debug for Collection {
467    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
468        let oratype = self.objtype.element_oracle_type().unwrap();
469        write!(f, "Collection({} collection of {}: ", self.objtype, oratype)?;
470        if let Ok(index) = self.first_index() {
471            let mut idx = index;
472            loop {
473                write_literal(f, &self.get(idx), oratype)?;
474                if let Ok(index) = self.next_index(idx) {
475                    idx = index;
476                    write!(f, ", ")?;
477                } else {
478                    break;
479                }
480            }
481        }
482        write!(f, ")")
483    }
484}
485
486impl AssertSend for Collection {}
487
488/// Oracle-specific object data type
489///
490/// ```no_run
491/// # use oracle::*;
492/// let conn = Connection::connect("scott", "tiger", "")?;
493///
494/// // MDSYS.SDO_GEOMETRY
495/// // https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-683FF8C5-A773-4018-932D-2AF6EC8BC119
496/// let geom_type = conn.object_type("MDSYS.SDO_GEOMETRY")?;
497/// let point_type = conn.object_type("MDSYS.SDO_POINT_TYPE")?;
498///
499/// // Create a new object
500/// let mut obj = geom_type.new_object()?;
501/// let mut point = point_type.new_object()?;
502/// point.set("X", &-79)?;
503/// point.set("Y", &37)?;
504/// obj.set("SDO_GTYPE", &2001)?;
505/// obj.set("SDO_POINT", &point)?;
506/// assert_eq!(obj.to_string(), "MDSYS.SDO_GEOMETRY(2001, NULL, MDSYS.SDO_POINT_TYPE(-79, 37, NULL), NULL, NULL)");
507///
508/// // Gets an attribute value.
509/// let gtype: i32 = obj.get("SDO_GTYPE")?;
510/// assert_eq!(gtype, 2001);
511/// # Ok::<(), Error>(())
512/// ```
513///
514/// Note: Methods in the type may be changed in future.
515pub struct Object {
516    conn: Conn,
517    pub(crate) handle: DpiObject,
518    objtype: ObjectType,
519}
520
521impl Object {
522    pub(crate) fn new(conn: Conn, handle: DpiObject, objtype: ObjectType) -> Object {
523        Object {
524            conn,
525            handle,
526            objtype,
527        }
528    }
529
530    pub(crate) fn ctxt(&self) -> &Context {
531        self.conn.ctxt()
532    }
533
534    pub(crate) fn handle(&self) -> *mut dpiObject {
535        self.handle.raw
536    }
537
538    /// Returns type information.
539    pub fn object_type(&self) -> &ObjectType {
540        &self.objtype
541    }
542
543    fn type_attr(&self, name: &str) -> Result<&ObjectTypeAttr> {
544        for attr in self.objtype.attributes() {
545            if attr.name() == name {
546                return Ok(attr);
547            }
548        }
549        Err(Error::invalid_attribute_name(name))
550    }
551
552    pub(crate) fn get_by_attr<T>(&self, attr: &ObjectTypeAttr) -> Result<T>
553    where
554        T: FromSql,
555    {
556        let mut data = unsafe { mem::zeroed() };
557        let mut buf = [0 as c_char; DPI_NUMBER_AS_TEXT_CHARS as usize];
558        match &attr.oratype {
559            &OracleType::Number(_, _) | &OracleType::Float(_) => unsafe {
560                dpiData_setBytes(&mut data, buf.as_mut_ptr(), buf.len() as u32);
561            },
562            _ => (),
563        }
564        let res;
565        let native_type_num;
566        {
567            let sql_value = SqlValue::from_oratype(self.conn.clone(), &attr.oratype, &mut data)?;
568            native_type_num = sql_value.native_type_num();
569            chkerr!(
570                self.ctxt(),
571                dpiObject_getAttributeValue(
572                    self.handle(),
573                    attr.handle.raw(),
574                    native_type_num,
575                    sql_value.data()?
576                )
577            );
578            res = sql_value.get();
579        }
580        unsafe { release_dpi_data(&data, native_type_num) };
581        res
582    }
583
584    /// Gets an value at the specified attribute.
585    pub fn get<T>(&self, name: &str) -> Result<T>
586    where
587        T: FromSql,
588    {
589        self.get_by_attr(self.type_attr(name)?)
590    }
591
592    /// Sets the value to the specified attribute.
593    pub fn set(&mut self, name: &str, value: &dyn ToSql) -> Result<()> {
594        let attrtype = self.type_attr(name)?;
595        let mut data = unsafe { mem::zeroed() };
596        let mut sql_value =
597            SqlValue::from_oratype(self.conn.clone(), &attrtype.oratype, &mut data)?;
598        sql_value.set(value)?;
599        chkerr!(
600            self.ctxt(),
601            dpiObject_setAttributeValue(
602                self.handle(),
603                attrtype.handle.raw(),
604                sql_value.native_type_num(),
605                sql_value.data()?
606            )
607        );
608        Ok(())
609    }
610}
611
612impl Clone for Object {
613    fn clone(&self) -> Object {
614        Object::new(self.conn.clone(), self.handle.clone(), self.objtype.clone())
615    }
616}
617
618impl FromSql for Object {
619    fn from_sql(val: &SqlValue) -> Result<Object> {
620        val.to_object()
621    }
622}
623
624impl ToSql for Object {
625    fn oratype(&self, _conn: &Connection) -> Result<OracleType> {
626        Ok(OracleType::Object(self.object_type().clone()))
627    }
628    fn to_sql(&self, val: &mut SqlValue) -> Result<()> {
629        val.set_object(self)
630    }
631}
632
633impl fmt::Display for Object {
634    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
635        write!(f, "{}(", self.objtype)?;
636        let mut first = true;
637        for attr in self.objtype.attributes() {
638            if first {
639                first = false;
640            } else {
641                write!(f, ", ")?;
642            }
643            write_literal(f, &self.get_by_attr(attr), &attr.oratype)?;
644        }
645        write!(f, ")")
646    }
647}
648
649impl fmt::Debug for Object {
650    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
651        write!(f, "Object({}(", self.objtype)?;
652        let mut first = true;
653        for attr in self.objtype.attributes() {
654            if first {
655                first = false;
656            } else {
657                write!(f, ", ")?;
658            }
659            write!(f, "{}({}): ", attr.name(), attr.oracle_type())?;
660            write_literal(f, &self.get_by_attr(attr), &attr.oratype)?;
661        }
662        write!(f, "))")
663    }
664}
665
666impl AssertSend for Object {}
667
668/// Type information about Object or Collection data type
669///
670/// This is for not only Object type information but also
671/// collection type information.
672///
673/// # Examples
674///
675/// Gets MDSYS.SDO_GEOMETRY object type information.
676///
677/// ```no_run
678/// # use oracle::*;
679/// let conn = Connection::connect("scott", "tiger", "")?;
680/// let objtype = conn.object_type("MDSYS.SDO_GEOMETRY");
681/// # Ok::<(), Error>(())
682/// ```
683///
684/// Gets object type infomration in query.
685///
686/// ```no_run
687/// # use oracle::*; use oracle::sql_type::*;
688/// let conn = Connection::connect("scott", "tiger", "")?;
689/// // conn.execute("create table location (name varchar2(60), loc sdo_geometry)", &[]);
690/// let mut stmt = conn
691///     .statement("select loc from location where name = '...'")
692///     .build()?;
693/// let rows = stmt.query(&[])?;
694/// let objtype = if let OracleType::Object(ref objtype) = *rows.column_info()[0].oracle_type() {
695///     objtype
696/// } else {
697///     panic!("Not an object type")
698/// };
699/// # Ok::<(), Error>(())
700/// ```
701#[derive(Clone)]
702pub struct ObjectType {
703    pub(crate) internal: Arc<ObjectTypeInternal>,
704}
705
706impl ObjectType {
707    pub(crate) fn from_dpi_object_type(conn: Conn, handle: DpiObjectType) -> Result<ObjectType> {
708        Ok(ObjectType {
709            internal: Arc::new(ObjectTypeInternal::from_dpi_object_type(conn, handle)?),
710        })
711    }
712
713    pub(crate) fn handle(&self) -> &DpiObjectType {
714        &self.internal.handle
715    }
716
717    /// Gets schema name
718    pub fn schema(&self) -> &str {
719        &self.internal.schema
720    }
721
722    /// Gets object name
723    pub fn name(&self) -> &str {
724        &self.internal.name
725    }
726
727    /// Gets package name if it is a PL/SQL type.
728    /// Otherwise, `None`.
729    pub fn package_name(&self) -> Option<&str> {
730        if let Some(ref pkg_name) = self.internal.package_name {
731            Some(pkg_name)
732        } else {
733            None
734        }
735    }
736
737    /// True when it is a collectoin. Otherwise false.
738    pub fn is_collection(&self) -> bool {
739        self.internal.elem_oratype.is_some()
740    }
741
742    /// Gets the Oracle type of elements if it is a collection.
743    /// Otherwise, `None`.
744    pub fn element_oracle_type(&self) -> Option<&OracleType> {
745        if let Some(ref oratype) = self.internal.elem_oratype {
746            Some(oratype)
747        } else {
748            None
749        }
750    }
751
752    /// Gets the number of attributes if it isn't a collection.
753    /// Otherwise, 0.
754    pub fn num_attributes(&self) -> usize {
755        self.internal.attrs.len()
756    }
757
758    /// Gets a vector of attribute information if it isn't a collection.
759    /// Otherwise, a zero-length vector.
760    ///
761    /// # Examples
762    ///
763    /// Prints attribute information of `MDSYS.SDO_GEOMETRY`.
764    ///
765    /// ```no_run
766    /// # use oracle::*;
767    /// let conn = Connection::connect("scott", "tiger", "")?;
768    /// let objtype = conn.object_type("MDSYS.SDO_GEOMETRY")?;
769    /// for attr in objtype.attributes() {
770    ///     println!("{:-20} {}", attr.name(), attr.oracle_type());
771    /// }
772    /// # Ok::<(), Error>(())
773    /// ```
774    pub fn attributes(&self) -> &[ObjectTypeAttr] {
775        &self.internal.attrs
776    }
777
778    /// Create a new Oracle object.
779    pub fn new_object(&self) -> Result<Object> {
780        if self.is_collection() {
781            return Err(Error::invalid_operation(format!(
782                "{}.{} isn't object type.",
783                self.schema(),
784                self.name()
785            )));
786        }
787        let conn = &self.internal.conn;
788        let mut handle = DpiObject::null();
789        chkerr!(
790            conn.ctxt(),
791            dpiObjectType_createObject(self.internal.handle.raw(), &mut handle.raw)
792        );
793        Ok(Object::new(conn.clone(), handle, self.clone()))
794    }
795
796    /// Create a new collection.
797    pub fn new_collection(&self) -> Result<Collection> {
798        if !self.is_collection() {
799            return Err(Error::invalid_operation(format!(
800                "{}.{} isn't collection type.",
801                self.schema(),
802                self.name()
803            )));
804        }
805        let conn = &self.internal.conn;
806        let mut handle = DpiObject::null();
807        chkerr!(
808            conn.ctxt(),
809            dpiObjectType_createObject(self.internal.handle.raw(), &mut handle.raw)
810        );
811        Ok(Collection::new(conn.clone(), handle, self.clone()))
812    }
813}
814
815impl cmp::PartialEq for ObjectType {
816    fn eq(&self, other: &Self) -> bool {
817        self.internal == other.internal
818    }
819}
820
821impl fmt::Display for ObjectType {
822    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
823        write!(f, "{}", self.internal)
824    }
825}
826
827impl fmt::Debug for ObjectType {
828    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
829        write!(f, "{:?}", self.internal)
830    }
831}
832
833/// Object type attribute information
834///
835/// See [ObjectType.attributes()](struct.ObjectType.html#method.attributes)
836pub struct ObjectTypeAttr {
837    conn: Conn,
838    handle: DpiObjectAttr,
839    name: String,
840    oratype: OracleType,
841}
842
843impl ObjectTypeAttr {
844    fn new(conn: Conn, handle: DpiObjectAttr) -> Result<ObjectTypeAttr> {
845        let mut info = MaybeUninit::uninit();
846        chkerr!(
847            conn.ctxt(),
848            dpiObjectAttr_getInfo(handle.raw(), info.as_mut_ptr())
849        );
850        let info = unsafe { info.assume_init() };
851        Ok(ObjectTypeAttr {
852            oratype: OracleType::from_type_info(&conn, &info.typeInfo)?,
853            conn,
854            handle,
855            name: to_rust_str(info.name, info.nameLength),
856        })
857    }
858
859    /// Gets the attribute name
860    pub fn name(&self) -> &str {
861        &self.name
862    }
863
864    /// Gets the attribute type
865    pub fn oracle_type(&self) -> &OracleType {
866        &self.oratype
867    }
868}
869
870impl Clone for ObjectTypeAttr {
871    fn clone(&self) -> ObjectTypeAttr {
872        ObjectTypeAttr {
873            conn: self.conn.clone(),
874            handle: self.handle.clone(),
875            name: self.name.clone(),
876            oratype: self.oratype.clone(),
877        }
878    }
879}
880
881impl fmt::Debug for ObjectTypeAttr {
882    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
883        write!(
884            f,
885            "ObjectTypeAttr {{ handle: {:?}, name: {:?}, oratype: {:?} }}",
886            self.handle.raw(),
887            self.name,
888            self.oratype
889        )
890    }
891}
892
893//
894// ObjectTypeInternal
895//
896
897pub(crate) struct ObjectTypeInternal {
898    conn: Conn,
899    handle: DpiObjectType,
900    schema: String,
901    name: String,
902    package_name: Option<String>,
903    elem_oratype: Option<OracleType>,
904    attrs: Vec<ObjectTypeAttr>,
905}
906
907impl ObjectTypeInternal {
908    fn from_dpi_object_type(conn: Conn, handle: DpiObjectType) -> Result<ObjectTypeInternal> {
909        let mut info = MaybeUninit::uninit();
910        chkerr!(
911            conn.ctxt(),
912            dpiObjectType_getInfo(handle.raw(), info.as_mut_ptr())
913        );
914        let info = unsafe { info.assume_init() };
915        let (elem_oratype, attrs) = if info.isCollection != 0 {
916            match OracleType::from_type_info(&conn, &info.elementTypeInfo) {
917                Ok(oratype) => (Some(oratype), Vec::new()),
918                Err(err) => return Err(err),
919            }
920        } else {
921            let attrnum = info.numAttributes as usize;
922            let mut handles = Vec::<DpiObjectAttr>::with_capacity(attrnum);
923            chkerr!(
924                conn.ctxt(),
925                dpiObjectType_getAttributes(
926                    handle.raw(),
927                    info.numAttributes,
928                    // The following code works only when
929                    // the size of `*mut dpiObjectAttr` equals to that of `DpiObjectAttr`.
930                    handles.as_mut_ptr() as *mut *mut dpiObjectAttr
931                )
932            );
933            unsafe {
934                handles.set_len(attrnum);
935            }
936            let attrs: Result<Vec<_>> = handles
937                .into_iter()
938                .map(|handle| ObjectTypeAttr::new(conn.clone(), handle))
939                .collect();
940            (None, attrs?)
941        };
942        Ok(ObjectTypeInternal {
943            conn,
944            handle,
945            schema: to_rust_str(info.schema, info.schemaLength),
946            name: to_rust_str(info.name, info.nameLength),
947            package_name: if info.packageNameLength != 0 {
948                Some(to_rust_str(info.packageName, info.packageNameLength))
949            } else {
950                None
951            },
952            elem_oratype,
953            attrs,
954        })
955    }
956}
957
958impl cmp::PartialEq for ObjectTypeInternal {
959    fn eq(&self, other: &Self) -> bool {
960        self.handle.raw() == other.handle.raw()
961    }
962}
963
964impl fmt::Display for ObjectTypeInternal {
965    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
966        write!(f, "{}.{}", self.schema, self.name)
967    }
968}
969
970impl fmt::Debug for ObjectTypeInternal {
971    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
972        if self.elem_oratype.is_some() {
973            write!(
974                f,
975                "ObjectType({}.{} collection of {})",
976                self.schema,
977                self.name,
978                self.elem_oratype.as_ref().unwrap()
979            )
980        } else {
981            write!(f, "ObjectType({}.{}(", self.schema, self.name)?;
982            let mut first = true;
983            for attr in &self.attrs {
984                if first {
985                    first = false;
986                } else {
987                    write!(f, ", ")?;
988                }
989                write!(f, "{} {}", attr.name(), attr.oracle_type())?;
990            }
991            write!(f, "))")
992        }
993    }
994}