use std::cmp;
use std::fmt;
use std::ptr;
use std::sync::Arc;
use crate::binding::*;
use crate::chkerr;
use crate::sql_type::FromSql;
use crate::sql_type::OracleType;
use crate::sql_type::ToSql;
use crate::to_rust_str;
use crate::util::write_literal;
use crate::Connection;
use crate::Context;
use crate::DpiObjectAttr;
use crate::DpiObjectType;
use crate::Error;
use crate::Result;
use crate::SqlValue;
unsafe fn release_dpi_data(data: &dpiData, native_type_num: u32) {
if data.isNull == 0 {
match native_type_num {
DPI_NATIVE_TYPE_LOB => {
dpiLob_release(data.value.asLOB);
}
DPI_NATIVE_TYPE_OBJECT => {
dpiObject_release(data.value.asObject);
}
DPI_NATIVE_TYPE_ROWID => {
dpiRowid_release(data.value.asRowid);
}
_ => (),
}
}
}
pub struct Collection {
ctxt: &'static Context,
pub(crate) handle: *mut dpiObject,
objtype: ObjectType,
}
impl Collection {
pub(crate) fn new(
ctxt: &'static Context,
handle: *mut dpiObject,
objtype: ObjectType,
) -> Collection {
Collection {
ctxt: ctxt,
handle: handle,
objtype: objtype,
}
}
pub fn object_type(&self) -> &ObjectType {
&self.objtype
}
pub fn size(&self) -> Result<i32> {
let mut size = 0;
chkerr!(self.ctxt, dpiObject_getSize(self.handle, &mut size));
Ok(size)
}
pub fn first_index(&self) -> Result<i32> {
let mut index = 0;
let mut exists = 0;
chkerr!(
self.ctxt,
dpiObject_getFirstIndex(self.handle, &mut index, &mut exists)
);
if exists != 0 {
Ok(index)
} else {
Err(Error::NoDataFound)
}
}
pub fn last_index(&self) -> Result<i32> {
let mut index = 0;
let mut exists = 0;
chkerr!(
self.ctxt,
dpiObject_getLastIndex(self.handle, &mut index, &mut exists)
);
if exists != 0 {
Ok(index)
} else {
Err(Error::NoDataFound)
}
}
pub fn next_index(&self, index: i32) -> Result<i32> {
let mut next = 0;
let mut exists = 0;
chkerr!(
self.ctxt,
dpiObject_getNextIndex(self.handle, index, &mut next, &mut exists)
);
if exists != 0 {
Ok(next)
} else {
Err(Error::NoDataFound)
}
}
pub fn prev_index(&self, index: i32) -> Result<i32> {
let mut prev = 0;
let mut exists = 0;
chkerr!(
self.ctxt,
dpiObject_getPrevIndex(self.handle, index, &mut prev, &mut exists)
);
if exists != 0 {
Ok(prev)
} else {
Err(Error::NoDataFound)
}
}
pub fn exist(&self, index: i32) -> Result<bool> {
let mut exists = 0;
chkerr!(
self.ctxt,
dpiObject_getElementExistsByIndex(self.handle, index, &mut exists)
);
Ok(exists != 0)
}
pub fn get<T>(&self, index: i32) -> Result<T>
where
T: FromSql,
{
let oratype = self.objtype.element_oracle_type().unwrap();
let mut data = Default::default();
let mut buf = [0i8; 172];
if let OracleType::Number(_, _) = *oratype {
unsafe {
dpiData_setBytes(&mut data, buf.as_mut_ptr(), buf.len() as u32);
}
}
let sql_value = SqlValue::from_oratype(self.ctxt, oratype, &mut data)?;
let native_type_num = sql_value.native_type_num();
chkerr!(
self.ctxt,
dpiObject_getElementValueByIndex(self.handle, index, native_type_num, &mut data)
);
let res = sql_value.get();
unsafe { release_dpi_data(&data, native_type_num) };
res
}
pub fn set(&mut self, index: i32, value: &dyn ToSql) -> Result<()> {
let oratype = self.objtype.element_oracle_type().unwrap();
let mut data = Default::default();
let mut sql_value = SqlValue::from_oratype(self.ctxt, oratype, &mut data)?;
sql_value.set(value)?;
chkerr!(
self.ctxt,
dpiObject_setElementValueByIndex(
self.handle,
index,
sql_value.native_type_num(),
&mut data
)
);
Ok(())
}
pub fn push(&mut self, value: &dyn ToSql) -> Result<()> {
let oratype = self.objtype.element_oracle_type().unwrap();
let mut data = Default::default();
let mut sql_value = SqlValue::from_oratype(self.ctxt, oratype, &mut data)?;
sql_value.set(value)?;
chkerr!(
self.ctxt,
dpiObject_appendElement(self.handle, sql_value.native_type_num(), &mut data)
);
Ok(())
}
pub fn remove(&mut self, index: i32) -> Result<()> {
chkerr!(
self.ctxt,
dpiObject_deleteElementByIndex(self.handle, index)
);
Ok(())
}
pub fn trim(&mut self, len: usize) -> Result<()> {
chkerr!(self.ctxt, dpiObject_trim(self.handle, len as u32));
Ok(())
}
}
impl Clone for Collection {
fn clone(&self) -> Collection {
unsafe { dpiObject_addRef(self.handle) };
Collection::new(self.ctxt, self.handle, self.objtype.clone())
}
}
impl Drop for Collection {
fn drop(&mut self) {
let _ = unsafe { dpiObject_release(self.handle) };
}
}
impl FromSql for Collection {
fn from_sql(val: &SqlValue) -> Result<Collection> {
val.to_collection()
}
}
impl ToSql for Collection {
fn oratype(&self, _conn: &Connection) -> Result<OracleType> {
Ok(OracleType::Object(self.object_type().clone()))
}
fn to_sql(&self, val: &mut SqlValue) -> Result<()> {
val.set_collection(self)
}
}
impl fmt::Display for Collection {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}(", self.objtype)?;
if let Ok(index) = self.first_index() {
let mut idx = index;
let oratype = self.objtype.element_oracle_type().unwrap();
loop {
write_literal(f, &self.get(idx), oratype)?;
if let Ok(index) = self.next_index(idx) {
idx = index;
write!(f, ", ")?;
} else {
break;
}
}
}
write!(f, ")")
}
}
impl fmt::Debug for Collection {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let oratype = self.objtype.element_oracle_type().unwrap();
write!(f, "Collection({} collection of {}: ", self.objtype, oratype)?;
if let Ok(index) = self.first_index() {
let mut idx = index;
loop {
write_literal(f, &self.get(idx), oratype)?;
if let Ok(index) = self.next_index(idx) {
idx = index;
write!(f, ", ")?;
} else {
break;
}
}
}
write!(f, ")")
}
}
pub struct Object {
ctxt: &'static Context,
pub(crate) handle: *mut dpiObject,
objtype: ObjectType,
}
impl Object {
pub(crate) fn new(
ctxt: &'static Context,
handle: *mut dpiObject,
objtype: ObjectType,
) -> Object {
Object {
ctxt: ctxt,
handle: handle,
objtype: objtype,
}
}
pub fn object_type(&self) -> &ObjectType {
&self.objtype
}
fn type_attr(&self, name: &str) -> Result<&ObjectTypeAttr> {
for attr in self.objtype.attributes() {
if attr.name() == name {
return Ok(attr);
}
}
Err(Error::InvalidAttributeName(name.to_string()))
}
pub(crate) fn get_by_attr<T>(&self, attr: &ObjectTypeAttr) -> Result<T>
where
T: FromSql,
{
let mut data = Default::default();
let mut buf = [0i8; 172];
if let OracleType::Number(_, _) = attr.oratype {
unsafe {
dpiData_setBytes(&mut data, buf.as_mut_ptr(), buf.len() as u32);
}
}
let sql_value = SqlValue::from_oratype(self.ctxt, &attr.oratype, &mut data)?;
let native_type_num = sql_value.native_type_num();
chkerr!(
self.ctxt,
dpiObject_getAttributeValue(self.handle, attr.handle.raw(), native_type_num, &mut data)
);
let res = sql_value.get();
unsafe { release_dpi_data(&data, native_type_num) };
res
}
pub fn get<T>(&self, name: &str) -> Result<T>
where
T: FromSql,
{
self.get_by_attr(self.type_attr(name)?)
}
pub fn set(&mut self, name: &str, value: &dyn ToSql) -> Result<()> {
let attrtype = self.type_attr(name)?;
let mut data = Default::default();
let mut sql_value = SqlValue::from_oratype(self.ctxt, &attrtype.oratype, &mut data)?;
sql_value.set(value)?;
chkerr!(
self.ctxt,
dpiObject_setAttributeValue(
self.handle,
attrtype.handle.raw(),
sql_value.native_type_num(),
&mut data
)
);
Ok(())
}
}
impl Clone for Object {
fn clone(&self) -> Object {
unsafe { dpiObject_addRef(self.handle) };
Object::new(self.ctxt, self.handle, self.objtype.clone())
}
}
impl Drop for Object {
fn drop(&mut self) {
let _ = unsafe { dpiObject_release(self.handle) };
}
}
impl FromSql for Object {
fn from_sql(val: &SqlValue) -> Result<Object> {
val.to_object()
}
}
impl ToSql for Object {
fn oratype(&self, _conn: &Connection) -> Result<OracleType> {
Ok(OracleType::Object(self.object_type().clone()))
}
fn to_sql(&self, val: &mut SqlValue) -> Result<()> {
val.set_object(self)
}
}
impl fmt::Display for Object {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}(", self.objtype)?;
let mut first = true;
for attr in self.objtype.attributes() {
if first {
first = false;
} else {
write!(f, ", ")?;
}
write_literal(f, &self.get_by_attr(attr), &attr.oratype)?;
}
write!(f, ")")
}
}
impl fmt::Debug for Object {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Object({}(", self.objtype)?;
let mut first = true;
for attr in self.objtype.attributes() {
if first {
first = false;
} else {
write!(f, ", ")?;
}
write!(f, "{}({}): ", attr.name(), attr.oracle_type())?;
write_literal(f, &self.get_by_attr(attr), &attr.oratype)?;
}
write!(f, "))")
}
}
#[derive(Clone)]
pub struct ObjectType {
pub(crate) internal: Arc<ObjectTypeInternal>,
}
impl ObjectType {
pub(crate) fn from_dpi_object_type(
ctxt: &'static Context,
handle: DpiObjectType,
) -> Result<ObjectType> {
Ok(ObjectType {
internal: Arc::new(ObjectTypeInternal::from_dpi_object_type(ctxt, handle)?),
})
}
pub(crate) fn handle(&self) -> &DpiObjectType {
&self.internal.handle
}
pub fn schema(&self) -> &str {
&self.internal.schema
}
pub fn name(&self) -> &str {
&self.internal.name
}
pub fn is_collection(&self) -> bool {
self.internal.elem_oratype.is_some()
}
pub fn element_oracle_type(&self) -> Option<&OracleType> {
if let Some(ref oratype) = self.internal.elem_oratype {
Some(oratype)
} else {
None
}
}
pub fn num_attributes(&self) -> usize {
self.internal.attrs.len()
}
pub fn attributes(&self) -> &[ObjectTypeAttr] {
&self.internal.attrs
}
pub fn new_object(&self) -> Result<Object> {
if self.is_collection() {
return Err(Error::InvalidOperation(format!(
"{}.{} isn't object type.",
self.schema(),
self.name()
)));
}
let ctxt = self.internal.ctxt;
let mut handle = ptr::null_mut();
chkerr!(
ctxt,
dpiObjectType_createObject(self.internal.handle.raw(), &mut handle)
);
Ok(Object::new(ctxt, handle, self.clone()))
}
pub fn new_collection(&self) -> Result<Collection> {
if !self.is_collection() {
return Err(Error::InvalidOperation(format!(
"{}.{} isn't collection type.",
self.schema(),
self.name()
)));
}
let ctxt = self.internal.ctxt;
let mut handle = ptr::null_mut();
chkerr!(
ctxt,
dpiObjectType_createObject(self.internal.handle.raw(), &mut handle)
);
Ok(Collection::new(ctxt, handle, self.clone()))
}
}
impl cmp::PartialEq for ObjectType {
fn eq(&self, other: &Self) -> bool {
self.internal == other.internal
}
}
impl fmt::Display for ObjectType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.internal)
}
}
impl fmt::Debug for ObjectType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self.internal)
}
}
pub struct ObjectTypeAttr {
ctxt: &'static Context,
handle: DpiObjectAttr,
name: String,
oratype: OracleType,
}
impl ObjectTypeAttr {
fn new(ctxt: &'static Context, handle: DpiObjectAttr) -> Result<ObjectTypeAttr> {
let mut info = Default::default();
chkerr!(ctxt, dpiObjectAttr_getInfo(handle.raw(), &mut info));
Ok(ObjectTypeAttr {
ctxt: ctxt,
handle: handle,
name: to_rust_str(info.name, info.nameLength),
oratype: OracleType::from_type_info(ctxt, &info.typeInfo)?,
})
}
pub fn name(&self) -> &str {
&self.name
}
pub fn oracle_type(&self) -> &OracleType {
&self.oratype
}
}
impl Clone for ObjectTypeAttr {
fn clone(&self) -> ObjectTypeAttr {
ObjectTypeAttr {
ctxt: self.ctxt,
handle: self.handle.clone(),
name: self.name.clone(),
oratype: self.oratype.clone(),
}
}
}
impl fmt::Debug for ObjectTypeAttr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"ObjectTypeAttr {{ handle: {:?}, name: {:?}, oratype: {:?} }}",
self.handle.raw(),
self.name,
self.oratype
)
}
}
pub(crate) struct ObjectTypeInternal {
ctxt: &'static Context,
handle: DpiObjectType,
schema: String,
name: String,
elem_oratype: Option<OracleType>,
attrs: Vec<ObjectTypeAttr>,
}
impl ObjectTypeInternal {
fn from_dpi_object_type(
ctxt: &'static Context,
handle: DpiObjectType,
) -> Result<ObjectTypeInternal> {
let mut info = Default::default();
chkerr!(ctxt, dpiObjectType_getInfo(handle.raw(), &mut info));
let (elem_oratype, attrs) = if info.isCollection != 0 {
match OracleType::from_type_info(ctxt, &info.elementTypeInfo) {
Ok(oratype) => (Some(oratype), Vec::new()),
Err(err) => return Err(err),
}
} else {
let attrnum = info.numAttributes as usize;
let mut attr_handles = vec![ptr::null_mut(); attrnum];
chkerr!(
ctxt,
dpiObjectType_getAttributes(
handle.raw(),
info.numAttributes,
attr_handles.as_mut_ptr()
)
);
let mut attrs = Vec::with_capacity(attrnum);
for i in 0..attrnum {
match ObjectTypeAttr::new(ctxt, DpiObjectAttr::new(attr_handles[i])) {
Ok(attr) => attrs.push(attr),
Err(err) => {
for j in (i + 1)..attrnum {
unsafe {
dpiObjectAttr_release(attr_handles[j]);
}
}
return Err(err);
}
}
}
(None, attrs)
};
Ok(ObjectTypeInternal {
ctxt: ctxt,
handle: handle,
schema: to_rust_str(info.schema, info.schemaLength),
name: to_rust_str(info.name, info.nameLength),
elem_oratype: elem_oratype,
attrs: attrs,
})
}
}
impl cmp::PartialEq for ObjectTypeInternal {
fn eq(&self, other: &Self) -> bool {
self.handle.raw() == other.handle.raw()
}
}
impl fmt::Display for ObjectTypeInternal {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}.{}", self.schema, self.name)
}
}
impl fmt::Debug for ObjectTypeInternal {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.elem_oratype.is_some() {
write!(
f,
"ObjectType({}.{} collection of {})",
self.schema,
self.name,
self.elem_oratype.as_ref().unwrap()
)
} else {
write!(f, "ObjectType({}.{}(", self.schema, self.name)?;
let mut first = true;
for attr in &self.attrs {
if first {
first = false;
} else {
write!(f, ", ")?;
}
write!(f, "{} {}", attr.name(), attr.oracle_type())?;
}
write!(f, "))")
}
}
}