1

这个问题与我之前的问题有关:RaiseError (PERL, DBI) equivalent for unixODBC C API?

当我稍后隔离问题时,我将发布新问题,即更具体、更孤立且没有不必要的信息。


版本:unixODBC 2.3.0
库:unixODBC - C API

假设我有一个存储的 FUNCTION

CREATE FUNCTION "test".func() RETURNING LVARCHAR(1000);
set debug file to '/home/directory_does_not_exists/unknown.log';
trace off;
trace on;
trace off;
return 'result is set here';
END FUNCTION;

相同的主体,但在存储的 PROCEDURE中:

CREATE PROCEDURE "test".proc(pDummy SMALLINT)
set debug file to '/home/directory_does_not_exists/unknown.log';
trace off;
trace on;
LET pDummy = 2;
trace off;
END PROCEDURE;

正如你所看到的,它们是完全一样的。调试文件的路径错误,因此预计会出错。当我call func()Aqua Data Studio错误中执行时检测到:

Cannot open DEBUG file for SPL routine trace

对于call proc(1).

但是SQLExecute当我通过 unixODBC(使用)执行这两个调用时

execute procedure proc(1);

返回SQL_ERROR(这是预期的并且很好),而

execute function func();

返回SQL_SUCCESS.. 'result is set here'不返回,返回空字符串( ),而不是''..

执行call func()给出相同的结果,如execute function func();

调用SQLMoreResults返回SQL_NO_DATASQLFetch返回SQL_ERROR

有任何想法吗?

4

3 回答 3

1

我不使用 Informix,但我使用 Perl DBD::ODBC 以及 isql(用 C 编写)尝试的简单示例都返回错误:

use strict;
use warnings;
use DBI;

my $h = DBI->connect();
eval {
    $h->do(q/drop function fmje/);
};

$h->do(<<'EOS');
create function fmje (@p1 as int)
returns int
as
begin
    declare @a int;

    set @a = 'fred';
    return @p1;
end;
EOS

my $s = $h->prepare(q/{? = call fmje(?)/);
$s->bind_param_inout(1, \my $x, 10);
$s->bind_param(2, 1);
$s->execute;
print "return is ", ($x ? $x : "undef"), "\n";



isql -v baugi sa easysoft
+---------------------------------------+
| Connected!                            |
|                                       |
| sql-statement                         |
| help [tablename]                      |
| quit                                  |
|                                       |
+---------------------------------------+
SQL> {call fmje(1)}
[22005][unixODBC][Easysoft][SQL Server Driver 11.0][SQL Server]Conversion failed when converting the varchar value 'fred' to data type int.
[ISQL]ERROR: Could not SQLExecute
SQL>

Informix 对于函数的工作方式必须不同,或者您可能没有通过 Aqua Data Studio 使用通用 ODBC。

如果您看到 Perl 的错误,正如您在另一篇文章中所说的,那么按照我在那里的建议进行操作并添加:

[ODBC]
Trace=yes
TraceFile=/tmp/unixodbc.log

到 odbcinst.ini 文件的顶部并运行 Perl。然后向我们展示错误日志中的行。然后用 isql 重复,以便我们可以比较 ODBC 调用。

于 2011-07-27T16:21:57.493 回答
1

这可能与您正在使用的服务器版本有关(不太可能,但可能),或者与您正在使用的 API 有关。当我使用使用 ESQL/C (CSDK) 3.70.FC2 构建的(我的)sqlcmd 程序在 MacOS X 10.7 上测试 IDS 11.70.FC2 时,我得到:

$ sqlcmd -c -d stores -e begin -xf x1.sql -e 'execute procedure proc(2)' \
         -e 'execute function func()' 
+ CREATE FUNCTION "test".func() RETURNING LVARCHAR(1000);
set debug file to '/home/directory_does_not_exists/unknown.log';
trace off;
trace on;
trace off;
return 'result is set here';
END FUNCTION;
+ CREATE PROCEDURE "test".proc(pDummy SMALLINT)
set debug file to '/home/directory_does_not_exists/unknown.log';
trace off;
trace on;
LET pDummy = 2;
trace off;
END PROCEDURE;
+ execute procedure proc(2)
SQL -648: Cannot open DEBUG file for SPL routine trace.
SQLSTATE: IX000 at /dev/stdin:0
+ execute function func()
SQL -648: Cannot open DEBUG file for SPL routine trace.
at /dev/stdin:0
$

如您所见,在 ESQL/C 中都func()正确proc()地报告了错误。因此,几乎可以肯定问题出在客户端代码中——在 ODBC 驱动程序及其处理错误的方式中,或者在调用 ODBC 驱动程序的代码中。

如何更多地隔离问题?

在环境中运行您的测试SQLIDEBUG=2:xyz。然后,找到名称开头的文件xyz_(例如,我得到了xyz_35424_0_819800)并运行sqliprint它。这将显示服务器是否两次生成错误消息。

我在一个跟踪中得到了两个与此类似的数据包:

S->C (12)               Time: 2011-07-28 00:28:02.41736
    SQ_ERR
        SQL error..........: -648
        ISAM/RSAM error....: 0
        Offset in statement: 0
        Error message......: "" [0]
    SQ_EOT

如果您看到两个带有 -648 错误的数据包,那么您就知道问题出在客户端处理错误的方式上。如果您没有看到这两个错误,那么我很想知道发生了什么。

于 2011-07-28T07:31:33.950 回答
0

首先 - 非常感谢@Jonathan Leffler(在他的机器上使用SQLIDEBUG=2:xyz+的提示sqliprint和测试)和 @bohica(使用 的提示strace)的支持!这真的帮助我找到了真正的问题并解决了它!两者都来自我+1。
不幸的是,答案不在他们的帖子中,这就是为什么我会自己回答。


概括:

SQLPrepare有时会某些错误而SQLExecute失败,但不是全部。使用存储过程时,这些函数会捕获更多错误。不幸的是,存储函数的情况有所不同。

我现在如何捕捉错误?如果SQLExecute成功,我打电话SQLNumResultCols- 这很正常。在那之后,我打电话SQLFetch也是意料之中的。但是,由于SQLFetch许多原因可能会失败(例如,它总是在存储过程上失败),它的错误被忽略了。还有一个while

if ( SQLNumResultCols( stmt, &nAllCols ) != SQL_SUCCESS )
// ...

int nSucceededFetches = 0; // added now, see below why
while ( SQL_SUCCEEDED( SQLFetch( stmt ) ) )
{
    ++nSucceededFetches; // added now, see below why
    /* bla bla */ 
}

是关键- 添加额外的检查:

if( 0 == nSucceededFetches && nColumns > 0 )

它说 - 如果在 FIRST 调用中有返回的列并且 fetch 失败,那么就出了问题。然后我有

while ( SQL_SUCCESS == SQLError( 0, 0, stmt, szSqlState, &nNativeError, szError, 500, &nErrorMsg ) )
{ /* bla bla */ }

一切都很好。我仍然不明白为什么会SQLExecute返回SQL_SUCCESS(甚至没有SQL_SUCCESS_WITH_INFO..),但这没关系。

于 2011-08-01T13:34:03.087 回答