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; // TODO: Why does this fail? // assert_eq!( // element_type_info.oracle_type_num, // enums::ODPIOracleTypeNum::Max // ); 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()?; // Create an object of this type. let created_obj = object_type.create()?; let _created: Object = created_obj; // let (first_idx, exists) = created.get_first_index()?; // assert_eq!(first_idx, 0); // assert_eq!(exists, 1); // Get the object value out of the query. 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, )?; // Query with object 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) }