You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 23:34:54