如何在Rust中调用含UDT输入输出参数的Oracle存储过程?
在Rust中调用带IN/OUT类型UDT参数的Oracle存储过程
可以调用这类存储过程,但你的调用方式和UDT的Trait实现需要调整,以下是具体的实现方向:
一、存储过程的正确调用方式
你目前用query_as的写法不对,Oracle存储过程的OUT参数不能用NULL占位,需要用PL/SQL块封装调用,通过execute方法绑定IN/OUT参数:
// 初始化OUT参数的空结构体 let mut out_bookable_type = BOOKABLETYPE::default(); // 用PL/SQL块封装存储过程调用 connection.execute( "BEGIN myStoredProcedure(:1, :2); END;", &[ &aso_bookable as &(dyn ToSql), // IN参数:ASO_BOOKABLE实例 &mut out_bookable_type as &mut (dyn FromSql), // OUT参数:接收结果的BOOKABLETYPE实例 ], )?; // 调用完成后,out_bookable_type中就存储了存储过程返回的结果
query_as主要用于处理SELECT查询语句,执行存储过程需要用execute方法配合PL/SQL块,才能正确处理OUT参数的绑定。
二、UDT与嵌套表的ToSql/FromSql实现要点
需要从最底层的结构体开始逐层实现Trait,因为上层UDT依赖下层的类型转换逻辑:
1. 基础UDT实现(以BOOKABLE为例)
假设Oracle中的BOOKABLE类型定义为:
CREATE TYPE BOOKABLE AS OBJECT ( ifi_id VARCHAR2(50), head_id VARCHAR2(50), forgtet_id VARCHAR2(50) );
对应的Rust实现:
use oracle::{ToSql, FromSql, Result, sql_type::OracleType}; #[derive(Debug, Default)] struct BOOKABLE { ifi_id: String, head_id: String, forgtet_id: String, } impl ToSql for BOOKABLE { fn oratype(&self, _conn: &oracle::Connection) -> Result<OracleType> { // 匹配数据库中UDT的名称(注意大小写一致) Ok(OracleType::Object("BOOKABLE".into())) } fn to_sql(&self, val: &mut oracle::SqlValue) -> Result<()> { let mut obj = val.new_object("BOOKABLE")?; // 按数据库属性名设置值 obj.set("IFI_ID", &self.ifi_id)?; obj.set("HEAD_ID", &self.head_id)?; obj.set("FORGTET_ID", &self.forgtet_id)?; val.set_object(obj) } } impl FromSql for BOOKABLE { fn from_sql(val: &oracle::SqlValue) -> Result<Self> { let obj = val.get_object()?; Ok(Self { ifi_id: obj.get("IFI_ID")?, head_id: obj.get("HEAD_ID")?, forgtet_id: obj.get("FORGTET_ID")?, }) } }
2. 嵌套表类型实现(以BOOKABLETABTYPE为例)
Oracle中的嵌套表定义:
CREATE TYPE BOOKABLETABTYPE AS TABLE OF BOOKABLE;
对应的Rust实现:
#[derive(Debug, Default)] struct BOOKABLETABTYPE { values: Vec<BOOKABLE>, } impl ToSql for BOOKABLETABTYPE { fn oratype(&self, _conn: &oracle::Connection) -> Result<OracleType> { Ok(OracleType::Collection("BOOKABLETABTYPE".into())) } fn to_sql(&self, val: &mut oracle::SqlValue) -> Result<()> { let mut coll = val.new_collection("BOOKABLETABTYPE")?; for item in &self.values { coll.push(item)?; } val.set_collection(coll) } } impl FromSql for BOOKABLETABTYPE { fn from_sql(val: &oracle::SqlValue) -> Result<Self> { let coll = val.get_collection()?; let mut values = Vec::new(); for i in 0..coll.len()? { let item: BOOKABLE = coll.get(i)?; values.push(item); } Ok(Self { values }) } }
3. 嵌套UDT实现(以BOOKABLETYPE为例)
Oracle中的类型定义:
CREATE TYPE BOOKABLETYPE AS OBJECT ( bookables BOOKABLETABTYPE, STATUS VARCHAR2(20) );
对应的Rust实现:
#[derive(Debug, Default)] struct BOOKABLETYPE { bookables: BOOKABLETABTYPE, STATUS: String, } impl ToSql for BOOKABLETYPE { fn oratype(&self, _conn: &oracle::Connection) -> Result<OracleType> { Ok(OracleType::Object("BOOKABLETYPE".into())) } fn to_sql(&self, val: &mut oracle::SqlValue) -> Result<()> { let mut obj = val.new_object("BOOKABLETYPE")?; obj.set("BOOKABLES", &self.bookables)?; obj.set("STATUS", &self.STATUS)?; val.set_object(obj) } } impl FromSql for BOOKABLETYPE { fn from_sql(val: &oracle::SqlValue) -> Result<Self> { let obj = val.get_object()?; Ok(Self { bookables: obj.get("BOOKABLES")?, STATUS: obj.get("STATUS")?, }) } }
4. 同理实现ASO_BOOKABLE与FORGTYPE
按照上述逻辑,先实现底层的FORGTYPE(嵌套表),再实现上层的ASO_BOOKABLE:
#[derive(Debug, Default)] struct FORGTYPE { values: Vec<i8>, } impl ToSql for FORGTYPE { fn oratype(&self, _conn: &oracle::Connection) -> Result<OracleType> { Ok(OracleType::Collection("FORGTYPE".into())) } fn to_sql(&self, val: &mut oracle::SqlValue) -> Result<()> { let mut coll = val.new_collection("FORGTYPE")?; for v in &self.values { coll.push(v)?; } val.set_collection(coll) } } #[derive(Debug, Default)] struct ASO_BOOKABLE { aso_id: String, forg_types: FORGTYPE, } impl ToSql for ASO_BOOKABLE { fn oratype(&self, _conn: &oracle::Connection) -> Result<OracleType> { Ok(OracleType::Object("ASO_BOOKABLE".into())) } fn to_sql(&self, val: &mut oracle::SqlValue) -> Result<()> { let mut obj = val.new_object("ASO_BOOKABLE")?; obj.set("ASO_ID", &self.aso_id)?; obj.set("FORG_TYPES", &self.forg_types)?; val.set_object(obj) } }
(注:ASO_BOOKABLE作为IN参数,仅需实现ToSql;若需作为OUT参数,再补充FromSql实现即可)
三、关键注意事项
- UDT名称大小写:数据库中创建的UDT名称如果是大写,Rust代码中必须用大写字符串匹配,否则会出现类型找不到的错误。
- 实现顺序:必须从最底层的UDT/嵌套表开始实现Trait,上层UDT依赖下层的类型转换逻辑,否则编译会报错。
- 属性名匹配:UDT的属性名必须和数据库中的定义完全一致(包括大小写),否则
get/set操作会失败。
内容的提问来源于stack exchange,提问作者M0653S
相关产品推荐
相关产品推荐

