use chrono::{TimeZone, Utc};
use mimir::enums;
use mimir::flags;
use mimir::Result;
use mimir::{
Connection, Context, ODPIData, ODPIObjectAttrInfo, ODPIObjectTypeInfo, ODPIStr, Object,
ObjectAttr, ObjectType, Statement,
};
use CREDS;
fn validate_object_attr_info(idx: usize, attr_info: &ODPIObjectAttrInfo) -> Result<()> {
let name_s = ODPIStr::new(attr_info.name, attr_info.name_length);
let name: String = name_s.into();
match idx {
0 => assert_eq!(name, "NUMBERVALUE"),
1 => assert_eq!(name, "STRINGVALUE"),
2 => assert_eq!(name, "FIXEDCHARVALUE"),
3 => assert_eq!(name, "DATEVALUE"),
4 => assert_eq!(name, "TIMESTAMPVALUE"),
5 => assert_eq!(name, "SUBOBJECTVALUE"),
6 => assert_eq!(name, "SUBOBJECTARRAY"),
_ => assert!(false),
}
Ok(())
}
fn validate_object_type_info(type_info: &ODPIObjectTypeInfo) -> Result<()> {
let schema = ODPIStr::new(type_info.schema, type_info.schema_length);
let name = ODPIStr::new(type_info.name, type_info.name_length);
let schema_str: String = schema.into();
let name_str: String = name.into();
assert_eq!(schema_str, "ODPIC");
assert_eq!(name_str, "UDT_OBJECT");
assert_eq!(type_info.is_collection, 0);
assert_eq!(type_info.num_attributes, 7);
let element_type_info = type_info.element_type_info;
assert_eq!(
element_type_info.default_native_type_num,
enums::ODPINativeTypeNum::Invalid
);
assert!(element_type_info.object_type.is_null());
Ok(())
}
fn validate_bytes(idx: usize, attr_data: &ODPIData) -> Result<()> {
let data_bytes = unsafe { attr_data.value.as_bytes };
let o_str = ODPIStr::new(data_bytes.ptr, data_bytes.length);
let data_str: String = o_str.into();
if idx == 1 {
assert_eq!(data_str, "First row");
} else if idx == 2 {
assert_eq!(data_str, "First ");
} else {
assert!(false);
}
Ok(())
}
fn validate_double(idx: usize, attr_data: &ODPIData) -> Result<()> {
if idx == 0 {
unsafe { assert!(attr_data.value.as_double - 1.0 < ::std::f64::EPSILON) };
} else {
assert!(false);
}
Ok(())
}
fn validate_timestamp(idx: usize, attr_data: &ODPIData) -> Result<()> {
let odpi_ts = unsafe { attr_data.value.as_timestamp };
let y = i32::from(odpi_ts.year);
let m = u32::from(odpi_ts.month);
let d = u32::from(odpi_ts.day);
let h = u32::from(odpi_ts.hour);
let mi = u32::from(odpi_ts.minute);
let se = u32::from(odpi_ts.second);
let ts = Utc.ymd(y, m, d).and_hms_nano(h, mi, se, odpi_ts.fsecond);
if idx == 3 {
let expected = Utc.ymd(2007, 3, 6).and_hms_nano(0, 0, 0, 0);
assert_eq!(ts, expected);
} else if idx == 4 {
let expected = Utc.ymd(2008, 9, 12).and_hms_nano(16, 40, 0, 0);
assert_eq!(ts, expected);
} else {
assert!(false);
}
Ok(())
}
fn validate_subobject(obj_type: &ObjectType) -> Result<()> {
let type_info = obj_type.get_info()?;
let schema = ODPIStr::new(type_info.schema, type_info.schema_length);
let name = ODPIStr::new(type_info.name, type_info.name_length);
let schema_str: String = schema.into();
let name_str: String = name.into();
assert_eq!(schema_str, "ODPIC");
assert_eq!(name_str, "UDT_SUBOBJECT");
assert_eq!(type_info.is_collection, 0);
assert_eq!(type_info.num_attributes, 2);
Ok(())
}
fn validate_objectarr(obj_type: &ObjectType) -> Result<()> {
let type_info = obj_type.get_info()?;
let schema = ODPIStr::new(type_info.schema, type_info.schema_length);
let name = ODPIStr::new(type_info.name, type_info.name_length);
let schema_str: String = schema.into();
let name_str: String = name.into();
assert_eq!(schema_str, "ODPIC");
assert_eq!(name_str, "UDT_OBJECTARRAY");
assert_eq!(type_info.is_collection, 1);
assert_eq!(type_info.num_attributes, 0);
let element_type_info = type_info.element_type_info;
assert_eq!(
element_type_info.oracle_type_num,
enums::ODPIOracleTypeNum::Object
);
assert_eq!(
element_type_info.default_native_type_num,
enums::ODPINativeTypeNum::Object
);
assert!(!element_type_info.object_type.is_null());
let arr_obj_type: ObjectType = element_type_info.object_type.into();
validate_subobject(&arr_obj_type)?;
Ok(())
}
fn validate_object(idx: usize, attr_info: &ODPIObjectAttrInfo, attr_data: &ODPIData) -> Result<()> {
let nested_obj_type_ptr = attr_info.type_info.object_type;
if nested_obj_type_ptr.is_null() {
assert!(false);
} else {
let nested_obj_type: ObjectType = nested_obj_type_ptr.into();
let odpi_obj_ptr = unsafe { attr_data.value.as_object };
let odpi_obj: Object = odpi_obj_ptr.into();
if idx == 5 {
validate_subobject(&nested_obj_type)?;
} else if idx == 6 {
validate_objectarr(&nested_obj_type)?;
let (first_index, first_index_exists) = odpi_obj.get_first_index()?;
assert_eq!(first_index, 0);
assert!(first_index_exists);
let (last_index, last_index_exists) = odpi_obj.get_last_index()?;
assert_eq!(last_index, 1);
assert!(last_index_exists);
let (next_index, next_index_exists) = odpi_obj.get_next_index(0)?;
assert_eq!(next_index, 1);
assert!(next_index_exists);
let (next_index_1, next_index_exists_1) = odpi_obj.get_next_index(1)?;
assert_eq!(next_index_1, 0);
assert!(!next_index_exists_1);
let (prev_index, prev_index_exists) = odpi_obj.get_prev_index(1)?;
assert_eq!(prev_index, 0);
assert!(prev_index_exists);
let (prev_index_1, prev_index_exists_1) = odpi_obj.get_prev_index(0)?;
assert_eq!(prev_index_1, 0);
assert!(!prev_index_exists_1);
let mut size = odpi_obj.get_size()?;
assert_eq!(size, 2);
odpi_obj.trim(1)?;
size = odpi_obj.get_size()?;
assert_eq!(size, 1);
}
}
Ok(())
}
fn validate_query_value(
idx: usize,
obj: &Object,
obj_attr: &ObjectAttr,
attr_info: &ODPIObjectAttrInfo,
) -> Result<()> {
let attr_data = obj.get_attribute_value(obj_attr, attr_info)?;
match attr_info.type_info.default_native_type_num {
enums::ODPINativeTypeNum::Bytes => validate_bytes(idx, &attr_data)?,
enums::ODPINativeTypeNum::Double => validate_double(idx, &attr_data)?,
enums::ODPINativeTypeNum::Timestamp => validate_timestamp(idx, &attr_data)?,
enums::ODPINativeTypeNum::Object => validate_object(idx, attr_info, &attr_data)?,
_ => {
assert!(false);
}
}
Ok(())
}
fn validate_object_type(object_col: &Statement, object_type: &ObjectType) -> Result<()> {
let attrs = object_type.get_attributes(7)?;
let mut obj_attrs = Vec::new();
let mut attr_infos = Vec::new();
for (idx, obj_attr) in attrs.iter().enumerate() {
let attr: ObjectAttr = (*obj_attr).into();
let attr_info = attr.get_info()?;
validate_object_attr_info(idx, &attr_info)?;
obj_attrs.push(attr);
attr_infos.push(attr_info);
}
let type_info = object_type.get_info()?;
validate_object_type_info(&type_info)?;
object_col.fetch()?;
let created_obj = object_type.create()?;
let _created: Object = created_obj;
let (object_col_type, object_col_data) = object_col.get_query_value(1)?;
assert_eq!(object_col_type, enums::ODPINativeTypeNum::Object);
let obj: Object = object_col_data.get_object().into();
for (idx, (obj_attr, attr_info)) in obj_attrs.iter().zip(attr_infos.iter()).enumerate() {
validate_query_value(idx, &obj, obj_attr, attr_info)?;
}
for obj_attr in obj_attrs {
obj_attr.release()?;
}
Ok(())
}
fn obj_type(ctxt: &Context) -> Result<()> {
let mut ccp = ctxt.init_common_create_params()?;
ccp.set_encoding("UTF-8")?;
ccp.set_nchar_encoding("UTF-8")?;
let conn = Connection::create(
ctxt,
Some(&CREDS[2]),
Some(&CREDS[3]),
Some("//oic.cbsnae86d3iv.us-east-2.rds.amazonaws.com/ORCL"),
Some(ccp),
None,
)?;
let object_col = conn.prepare_stmt(
Some(
"select ObjectCol \
from TestObjects \
order by IntCol",
),
None,
false,
)?;
let cols = object_col.execute(flags::DPI_MODE_EXEC_DEFAULT)?;
assert_eq!(cols, 1);
let query_info = object_col.get_query_info(1)?;
let type_info = query_info.type_info();
assert!(type_info.object_type().is_some());
if let Some(object_type) = type_info.object_type() {
validate_object_type(&object_col, &object_type)?;
}
object_col.close(None)?;
conn.close(flags::DPI_MODE_CONN_CLOSE_DEFAULT, None)?;
Ok(())
}
#[test]
fn objecttype() {
check_with_ctxt!(obj_type)
}