Oracle 存储过程返回 ref cursor 怎么写?TaoToken 供 Key,Codex 对照 pkg_test 排查
写 Oracle 存储过程返回数据集,真正让人卡住的往往不是那条 select 语句,而是包声明、包体、匿名块这三段之间的类型与签名必须严格对齐。pkg_test 这个最小例子里有 type myrctype is ref cursor、有 display(p_empno char, p_rc out myrctype) 这样的出参过程,还有匿名块里 w_rc fetch into w_empname 的循环读取,任何一处参数名、类型、绑定变量对不上,编译或运行就会直接报错。本文按接入配置的视角来写:先把 Codex 接到 TaoToken(官网 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=codex-config ),拿到 Key 和 Base URL,再把 pkg_test 三段代码贴给 Codex 逐段核对,最后回到本地 SQL*Plus 里跑 display('0001', w_rc) 验证 ref cursor 到底有没有按预期返回数据。TaoToken 在这里只负责提供模型调用的 Key 与 Base URL,它不替代 Oracle 存储过程本身,也不会替你编译包,代码正确与否仍以数据库里的编译结果和实际取数结果为准。
一、三段对不上:pkg_test 里 ref cursor 最常卡的现场
先还原场景。目标很简单:写一个存储过程,把一张 student 表里符合条件的记录,以 ref cursor 的形式返回给调用方,由调用方自己循环 fetch。
这个目标会拆成三段代码,分别放在三个地方执行:
第一段是包声明,只写接口,不写实现:
create or replace package pkg_test as type myrctype is ref cursor; procedure display(p_empno char, p_rc out myrctype); end pkg_test; /第二段是包体,写 procedure display 的具体逻辑。当 p_empno 为空时直接打开全表游标,不为空时用动态 SQL 加绑定变量过滤:
create or replace package body pkg_test as procedure display(p_empno char, p_rc out myrctype) is v_sql varchar2(200); begin if p_empno is null then open p_rc for select emp_name from student; else v_sql := 'select emp_name from student where emp_no = :w_empno'; open p_rc for v_sql using p_empno; end if; end display; end pkg_test; /第三段是匿名块调用,声明一个包内类型的变量接收游标,再一条条取出来打印:
set serveroutput on declare w_rc pkg_test.myrctype; w_empname student.emp_name%type; begin pkg_test.display('0001', w_rc); loop fetch w_rc into w_empname; exit when w_rc%notfound; dbms_output.put_line(w_empname); end loop; close w_rc; end; /三段代码单看都不难,问题在于它们之间有三条隐式契约。
第一条契约是类型归属。myrctype 定义在包的声明里,所以匿名块中声明变量必须写成 pkg_test.myrctype,而不是直接写 myrctype,也不能写成 sys_refcursor 混用,否则要么报标识符未声明,要么游标类型不匹配。
第二条契约是过程签名。包声明里的 display(p_empno char, p_rc out myrctype) 与包体里的过程头必须完全一致:参数名、参数顺序、参数模式 out、类型 myrctype,任何一项不同,包体就会编译不过,典型报错是 PLS-00323。
第三条契约是绑定变量。动态 SQL 字符串里写的是 :w_empno,那么 open ... for v_sql using p_empno 里的 using 后面就必须按顺序、按个数、按类型把值补上。少一个、多一个、类型对不上,都会在运行时抛 ORA-01008 或 ORA-06550 一类错误。这也是三段代码里最容易出错、又最难靠肉眼发现的地方,因为编译器不会在编译期替你检查字符串里的冒号变量。
二、TaoToken 前置:注册、建 Key、Codex 的接入位置
既然痛点集中在“三处细节对不上”和“动态 SQL 绑定易错”,一个可行的做法是把这三段代码交给 Codex,让它逐段做一致性核对:类型名是否统一、过程签名是否一致、using 的参数是否与 :w_empno 一一对应。这一步需要模型调用能力,而 Codex 需要配置一个可用的模型提供方。
接入动作本身很短,按顺序做三件事:
第一步,打开 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=codex-config 完成注册,进入控制台创建一个 API Key。Key 只在创建时完整展示,复制下来单独保存。
第二步,记下接口地址:https://taotoken.net/api 。注意这个地址后面不要接 /v1,因为 Codex 的 provider 配置会自己拼接具体路径;也不要带任何 UTM 查询参数,UTM 是给网页跳转统计用的,写进 base_url 会变成请求路径的一部分,导致 404 或参数污染。
第三步,在 Codex 的模型配置里把这个地址填进去。Codex 读的是 config.toml,下一节给出可直接复制的完整片段。
这里再强调一次边界:TaoToken 提供的是 Key 和 Base URL,也就是让 Codex 能发出模型请求;它不会替你编译 pkg_test,也不会在 Oracle 里建表建包。配通之后,代码怎么写、绑定变量怎么对、游标怎么 fetch,仍然由你在数据库里决定并验证。
三、可复制配置:config.toml + pkg_test 三段完整代码
Codex 的配置文件通常在用户目录下的 ~/.codex/config.toml。用自定义 provider 的方式接 TaoToken,可以写成下面这样:
model = "MODEL_ID" model_provider = "taotoken" [model_providers.taotoken] name = "TaoToken" base_url = "https://taotoken.net/api" env_key = "TAOTOKEN_API_KEY" wire_api = "chat"把 model 换成你在控制台看到的模型 ID,把 Key 放进环境变量,避免明文写进配置文件:
export TAOTOKEN_API_KEY=YOUR_API_KEY如果是在 Windows 下,用系统环境变量面板新建同名变量,或者在当前会话里设置对应变量,效果等价。配置完成后重新打开一个终端,让 Codex 重新读取 config.toml 与环境变量。
配置生效后,就可以把 pkg_test 的三段代码整段贴给 Codex,并明确提问方向,例如:
- 请核对包声明与包体的过程签名是否完全一致,参数名、参数模式、类型都逐项比对;
- 请核对匿名块里 w_rc 的类型是否用了包内限定名 pkg_test.myrctype;
- 请核对 open p_rc for v_sql using p_empno 与字符串中的 :w_empno 是否一一对应,是否存在多余或缺失的绑定;
- 请指出 fetch w_rc into w_empname 的循环里,%notfound 的判断位置是否会导致最后一行数据被漏掉或多打一行空值。
这类提问给的是比对规则,而不是让模型凭空生成代码,得到的结果更容易落地核对。特别提醒:模型给出的修改建议只是参考,最终仍要回到 Oracle 里编译执行。
四、验证:SQL*Plus 跑 display('0001', w_rc),再用 Codex 复核
配置通了不等于代码对了,验证必须分两层。
第一层是连通性验证。在终端里确认 Codex 能正常发起请求,比如给它一句简单指令,看是否有正常返回而不是 401、403 或连接超时。这一步只证明 Key 和 Base URL 配对了,不证明 SQL 正确。
第二层是数据库验证。打开 SQL*Plus,用你的账号连上目标库,按顺序执行:
-- 1. 先编译包声明 @pkg_test_spec.sql -- 2. 再编译包体 @pkg_test_body.sql -- 3. 检查编译状态 select object_name, object_type, status from user_objects where object_name = 'PKG_TEST';status 必须是 VALID,如果有 INVALID,先看 user_errors 里的具体行号和报错文本:
select name, type, line, position, text from user_errors where name = 'PKG_TEST' order by type, line;确认包和包体都有效之后,再执行匿名块,跑 display('0001', w_rc) 这条调用路径:
set serveroutput on size 1000000 declare w_rc pkg_test.myrctype; w_empname student.emp_name%type; begin pkg_test.display('0001', w_rc); loop fetch w_rc into w_empname; exit when w_rc%notfound; dbms_output.put_line('name=' || w_empname); end loop; close w_rc; end; /如果屏幕按行输出了 emp_no 为 0001 的员工姓名,说明 ref cursor 的返回逻辑是通的。如果一行都没有,把 display 的第一个参数换成 null 再跑一次,走 open p_rc for select emp_name from student 这条全量分支;两条分支都验过,才能确定问题是在动态 SQL 绑定上,还是在数据本身。
验证通过之后,把 user_errors 的输出、两次匿名块的执行结果、以及最终的包声明和包体代码一起贴回 Codex,让它做一次反向复核,重点看绑定变量和游标关闭这两处是否还有隐患。
五、本篇常见错排查
按报错文本对照,可以覆盖大部分现场:
第一类,PLS-00201: identifier 'PKG_TEST.MYRCTYPE' must be declared。说明包声明没编译成功,或者当前会话看到的还是旧版本对象。先查 user_objects 的 status,再查 user_errors,别急着改匿名块。
第二类,PLS-00323: subprogram or cursor 'DISPLAY' is declared in a package specification and must be defined in the package body。这是包体和包声明签名不一致的典型报错。逐字比对 p_empno 的参数名、char 类型、p_rc 的 out 模式与 myrctype 类型,尤其注意包体里是否多写了默认值。
第三类,ORA-01008: not all variables bound。动态 SQL 里有 :w_empno,但 using 后面的参数个数对不上,或者某个分支忘了加 using。检查 open p_rc for v_sql using p_empno 这一行是否只在 else 分支里,if 分支用的是静态 SQL 不需要 using。
第四类,ORA-00904: invalid identifier。多数是列表或表名写错,或者当前用户没有 student 表的查询权限。用 desc student 确认列名是 emp_name、emp_no 再继续。
第五类,匿名块执行成功但一行都不打印。先确认 set serveroutput on 是否执行过,再确认 exit when w_rc%notfound 的位置——它必须紧跟在 fetch 之后,判断在打印之前,否则容易出现多一行空值或漏掉数据。另外注意本文示例在循环后补了 close w_rc,显式关闭游标是好习惯,虽然会话结束也会释放。
第六类,Codex 报 401 或 404。401 通常是 Key 没放进环境变量,或者变量名与 config.toml 里 env_key 写的不一致;404 多半是 base_url 写成了 https://taotoken.net/api/v1,或者把 UTM 参数一起粘了进去。把 base_url 恢复成 https://taotoken.net/api 即可。
第七类,改了代码但报错没变。Oracle 的包是有状态的,包体重新编译后,当前会话可能还持有旧的包状态。执行 alter session 或重新登录一次,让会话重新加载包,再跑一次验证。
六、把 Key、Base URL 与文档一次配齐
本篇涉及的动作可以归结成两张清单。
一张是接入清单:TaoToken 官网注册、创建 API Key、在 Codex 的 config.toml 里用 [model_providers.taotoken] 配好 base_url 为 https://taotoken.net/api、env_key 指向环境变量、model 填控制台给出的模型 ID。Key 管理页面在 https://taotoken.net/console/api-keys?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=api-keys ,如果接入过程中对 base_url、wire_api、环境变量名有疑问,接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=doc ,两处对照看基本能把配置问题排掉。想先确认 Key 与模型是否真的可用,可以到模型对话页发一条最短请求验证:https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=chat 。
另一张是代码清单:pkg_test 的包声明、包体、匿名块三段必须同源同签名,动态 SQL 里的 :w_empno 与 using p_empno 必须一一对应,w_rc 的类型必须写成 pkg_test.myrctype,验证时以 SQL*Plus 里 display('0001', w_rc) 的实际输出为准,而不是以模型说“没问题”为准。
如果你打算长期用 Codex 做这类存储过程重构、包体一致性核对甚至批量改 SQL 的工作,可以了解一下 Coding Plan:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=coding-plan ,把 Key、Base URL 和日常编码链路固定下来,省去每次重新配环境的来回。