2

从我读过的有关 LOB 和 OCI8 PHP 扩展的文档中,我似乎应该调用$lob->close()下面的代码,因为我使用了$lob->writeTemporary(). $lob->close()当我将 LOB 传递给接受 IN 参数的存储过程时工作正常,但如果我将 LOB 传递给接受 IN OUT 参数的存储过程则不起作用。

显然我可以省略$lob->close()对 IN OUT 参数的调用,但我很想知道为什么我需要这样做。有人可以解释一下下面的代码中发生了什么导致它产生以下错误吗?非常感谢任何见解。

OCI-Lob::close() [oci-lob.close]: ORA-22289: 无法对未打开的文件或 LOB 执行 %s 操作

$my_clob = 'Lorem ipsum dolor sit amet...';

$connection = oci_connect('user', 'pass', 'connection string');
$statement  = oci_parse($connection, 'begin p_clob_in_out(:p_my_clob); end;');
$lob        = oci_new_descriptor($connection, OCI_D_LOB);

$lob->writeTemporary($my_clob, OCI_TEMP_CLOB);

oci_bind_by_name($statement, ':p_my_clob', $lob, -1, OCI_B_CLOB);

oci_execute($statement, OCI_DEFAULT);

if (is_object($lob))
{
  $data = $lob->load();

  $lob->close();
  $lob->free();
}

echo $data;

p_clob_in_out过程如下所示:

procedure p_clob_in_out(
    p_my_clob in out clob
)
is
begin
    p_my_clob := 'ABC123... ' || p_my_clob;
end p_clob_in_out;

感谢Vincent Malgrat 的回答,在进一步阅读后,我认为这就是正在发生的事情......在我的 PHP 代码中,该$lob变量是传入的临时 LOB。该临时 LOB 由过程修改,从而创建它的副本. 然后将副本传递出去并替换$lob变量。该writeTemporary方法从未在 LOB 的副本上调用过,因此当我调用$lob->close()它时会失败。$lob->close()PHP 脚本不再可以访问最初创建的原始 LOB(我可以调用它)。

我认为 NOCOPY 提示可能不适用于此处,因为在此页面上的“对 NOCOPY 的限制”下,它指出如果“通过数据库链接或作为外部过程调用子程序”,则 NOCOPY 将被忽略。根据this page,听起来我的PHP脚本中调用存储过程的匿名块将被视为外部过程。

4

1 回答 1

0

我遇到了与临时 LOB 类似的令人费解的问题(纯 Pl/SQL,因此在 PHP 中可能类似)。一些在持久 LOBS 上运行良好的代码不适用于临时 LOB。经过一番搜索,我在文档中找到了这个注释:

如果用户修改临时 LOB 而另一个定位器也指向它,则会创建临时 LOB 的副本。执行修改的定位器现在指向临时 LOB 的新副本。其他定位器不再看到与进行修改的定位器相同的数据。

如果您在程序中指定NOCOPY ,我很想知道您是否遇到同样的问题:procedure p_clob_in_out(p_my_clob in out NOCOPY clob)'ABC123... '您还可以在过程调用后检查您的 lob 是否包含吗?

我的推理如下:IN参数作为参考传递,因此当您将 LOB 作为 IN 参数传递时,它无论如何都会被修改。IN OUT参数是按值传递的,因此实际上您将过程应用于临时 LOB 的副本(不会深度复制持久 LOB)。

于 2011-12-20T16:54:49.663 回答