kubo / rust-oracle

Oracle driver for Rust
193 stars 43 forks source link

VARRAY #63

Closed raymclee closed 1 year ago

raymclee commented 2 years ago

Hi, How can I query varray type data to a struct and then convert it to vector of string? Thanks!

kubo commented 2 years ago

How about the following sample code. I'll add methods creating various Collection iterators later. One of them is same with the collection::Values iterator bellow except the visibility of collection::Values::new.

use oracle::sql_type::{Collection, OracleType};
use oracle::{Connection, Result};

fn main() -> Result<()> {
    let conn = Connection::connect("odpic", "welcome", "")?;

    // See https://github.com/oracle/odpi/blob/v4.4.1/test/sql/SetupTest.sql#L489-L492
    let objtype = conn.object_type("PKG_TESTSTRINGARRAYS.UDT_STRINGLIST")?;

    // See https://github.com/oracle/odpi/blob/v4.4.1/test/sql/SetupTest.sql#L553-L561
    let mut stmt = conn
        .statement("begin pkg_TestStringArrays.TestIndexBy(:1); end;")
        .build()?;
    stmt.execute(&[&OracleType::Object(objtype)])?;
    let coll: Collection = stmt.bind_value(1)?;

    let vec: Vec<String> = collection::Values::new(&coll).collect::<Result<Vec<_>>>()?;
    assert_eq!(vec[0], "First element".to_string());
    assert_eq!(vec[1], "Second element".to_string());
    assert_eq!(vec[2], "Third element".to_string());
    assert_eq!(vec[3], "Fourth element".to_string());
    Ok(())
}

mod collection {
    use oracle::sql_type::{Collection, FromSql};
    use oracle::{Error, Result};
    use std::marker::PhantomData;

    fn next_item<T>(coll: &Collection, index: Option<i32>) -> Result<Option<(i32, T)>>
    where
        T: FromSql,
    {
        let new_idx_result = if let Some(idx) = index {
            coll.next_index(idx)
        } else {
            coll.first_index()
        };
        match new_idx_result {
            Ok(new_idx) => {
                let obj = coll.get(new_idx)?;
                Ok(Some((new_idx, obj)))
            }
            Err(Error::NoDataFound) => Ok(None),
            Err(err) => Err(err),
        }
    }

    pub struct Values<'a, T> {
        coll: &'a Collection,
        index: Option<i32>,
        phantom: PhantomData<T>,
    }

    impl<'a, T> Values<'a, T> {
        pub fn new(coll: &Collection) -> Values<T> {
            Values {
                coll,
                index: None,
                phantom: PhantomData,
            }
        }
    }

    impl<'a, T> Iterator for Values<'a, T>
    where
        T: FromSql,
    {
        type Item = Result<T>;
        fn next(&mut self) -> Option<Result<T>> {
            match next_item(self.coll, self.index) {
                Ok(Some((idx, value))) => {
                    self.index = Some(idx);
                    Some(Ok(value))
                }
                Ok(None) => None,
                Err(err) => Some(Err(err)),
            }
        }
    }
}
raymclee commented 2 years ago

How about the following sample code. I'll add methods creating various Collection iterators later. One of them is same with the collection::Values iterator bellow except the visibility of collection::Values::new.

use oracle::sql_type::{Collection, OracleType};
use oracle::{Connection, Result};

fn main() -> Result<()> {
    let conn = Connection::connect("odpic", "welcome", "")?;

    // See https://github.com/oracle/odpi/blob/v4.4.1/test/sql/SetupTest.sql#L489-L492
    let objtype = conn.object_type("PKG_TESTSTRINGARRAYS.UDT_STRINGLIST")?;

    // See https://github.com/oracle/odpi/blob/v4.4.1/test/sql/SetupTest.sql#L553-L561
    let mut stmt = conn
        .statement("begin pkg_TestStringArrays.TestIndexBy(:1); end;")
        .build()?;
    stmt.execute(&[&OracleType::Object(objtype)])?;
    let coll: Collection = stmt.bind_value(1)?;

    let vec: Vec<String> = collection::Values::new(&coll).collect::<Result<Vec<_>>>()?;
    assert_eq!(vec[0], "First element".to_string());
    assert_eq!(vec[1], "Second element".to_string());
    assert_eq!(vec[2], "Third element".to_string());
    assert_eq!(vec[3], "Fourth element".to_string());
    Ok(())
}

mod collection {
    use oracle::sql_type::{Collection, FromSql};
    use oracle::{Error, Result};
    use std::marker::PhantomData;

    fn next_item<T>(coll: &Collection, index: Option<i32>) -> Result<Option<(i32, T)>>
    where
        T: FromSql,
    {
        let new_idx_result = if let Some(idx) = index {
            coll.next_index(idx)
        } else {
            coll.first_index()
        };
        match new_idx_result {
            Ok(new_idx) => {
                let obj = coll.get(new_idx)?;
                Ok(Some((new_idx, obj)))
            }
            Err(Error::NoDataFound) => Ok(None),
            Err(err) => Err(err),
        }
    }

    pub struct Values<'a, T> {
        coll: &'a Collection,
        index: Option<i32>,
        phantom: PhantomData<T>,
    }

    impl<'a, T> Values<'a, T> {
        pub fn new(coll: &Collection) -> Values<T> {
            Values {
                coll,
                index: None,
                phantom: PhantomData,
            }
        }
    }

    impl<'a, T> Iterator for Values<'a, T>
    where
        T: FromSql,
    {
        type Item = Result<T>;
        fn next(&mut self) -> Option<Result<T>> {
            match next_item(self.coll, self.index) {
                Ok(Some((idx, value))) => {
                    self.index = Some(idx);
                    Some(Ok(value))
                }
                Ok(None) => None,
                Err(err) => Some(Err(err)),
            }
        }
    }
}

that's work! Thanks. I have noticed the Collection and couldn't figure out how to use it 😅