使用 Pro*C,如何在不设置 'sqlcheck=semantics' 选项的情况下从 PL 存储函数中打开的游标中获取数据?

Using Pro*C, how to fetch from a cursor opened in PL stored function without setting 'sqlcheck=semantics' option?

我试图从在存储函数中打开的游标变量中获取数据,但我总是收到 "fetch out of sequence" 错误消息。

这是存储函数:

CREATE OR REPLACE FUNCTION test_function RETURN SYS_REFCURSOR AS
  p_recordset SYS_REFCURSOR;
BEGIN
  OPEN p_recordset FOR SELECT '1' FROM DUAL UNION SELECT '2' FROM DUAL;
  RETURN p_recordset;
END TEST_FUNCTION;

Pro*C 代码:

int myfunction()
{
  ...
  EXEC SQL BEGIN DECLARE SECTION;
  SQL_CURSOR sql_cursor_pl;
  VARCHAR string_field[20];
  EXEC SQL END DECLARE SECTION;

  EXEC SQL ALLOCATE :sql_cursor_pl;

  // It is not possible to use embedded PL/SQL block as seen in other 
  // examples because it requieres a compilation time access to database 
  // that I don't have, so as far as I know I have to use EXEC SQL CALL
  EXEC SQL CALL sch.test_function() INTO :sql_cursor_pl;

  EXEC SQL WHENEVER NOT FOUND DO break;

  //for ( ; ; )
  while (sqlca.sqlcode == '[=12=]')
  {
    EXEC SQL FETCH :sql_cursor_pl INTO :string_field;
    ...
  }

  EXEC SQL CLOSE :sql_cursor_pl;
  ...
}

如果我像下面这样在其他 PL 块中使用存储的函数,它就可以正常工作,所以我认为问题应该出在 Pro*C 代码中。

DECLARE
  mycursor sys_refcursor;
  string_field VARCHAR(20)
BEGIN
  mycursor := sch.test_function();
  LOOP
    FETCH mycursor INTO string_field;
    EXIT WHEN mycursor%NOTFOUND;
    DBMS_OUTPUT.PUT_LINE(string_field);
  END LOOP;
  CLOSE mycursor;
END;

问题出在执行FETCH命令之前测试的while条件。

我通常使用 EXEC SQL WHENEVER NOT FOUND 指令来解决这些循环。以下是有关如何操作的示例:

while(1)
{
    EXEC SQL WHENEVER NOT FOUND DO break; // Where a no data found occurs execute a C "break" instruction.
    EXEC SQL FETCH :sql_cursor_pl INTO :string_field;
    EXEC SQL WHENEVER NOT FOUND DO ???; // Restore desired behaviour.

    ...
}

第一条指令指示 Pro*C/C++ 在出现未发现数据错误时执行 break,这意味着退出 while 循环。 需要第二个指令来恢复所需的行为(此处需要在 while 内进行中断,但在其余代码中可能不是最佳选择)。

我想我已经找到了问题所在和一个解决方案。在 PL/SQL 块中打开游标时,游标索引从 0 开始,但 Pro*C 的从 1 开始。我认为问题是 Pro*C 根本不更新游标索引,因此一种解决方案可能是像这样手动增加 here.

...
while (sqlca.sqlcode == '[=10=]')
{
  sql_cursor_pl.curocn++;
  EXEC SQL FETCH :sql_cursor_pl INTO :string_field;
  ...
}
...