在存储的**过程**中检测到相同的错误,但在存储的**函数**中未检测到

Posted

技术标签:

【中文标题】在存储的**过程**中检测到相同的错误,但在存储的**函数**中未检测到【英文标题】:The same error is detected in stored **procedure**, but not in stored **function** 【发布时间】:2011-07-27 11:14:45 【问题描述】:

这个问题和我上一个问题有关:RaiseError (PERL, DBI) equivalent for unixODBC C API?

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


版本:unixODBC 2.3.0 库:unixODBC - C API

假设我有一个存储的函数

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;

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

Cannot open DEBUG file for SPL routine trace

call proc(1) 也一样。

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

execute procedure proc(1);

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

execute function func();

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

执行call func() 得到与execute function func(); 相同的结果

调用SQLMoreResults返回SQL_NO_DATASQLFetch返回SQL_ERROR

有什么想法吗?

【问题讨论】:

为什么这个问题被标记为 C? 因为它是关于unixODBCC API?对不起,我忘了在这里提。 【参考方案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 调用。

【讨论】:

哇!好的!有一点小进步。我创建了相同的函数并通过isql 执行它 - 未检测到错误.. 或者至少 - 执行得很好。但是,现在我看到了这个空字符串的来源——这是错误描述!哇。因此,当我运行 isq-v(详细)时,返回的字符串不是空的,但它包含错误描述 - 在本例中为:[37000][Informix][Informix ODBC Driver][Informix]Character to numeric conversion error。但是,它执行得很好,并且没有显示错误(与您的执行一样)-在我的 isqlINFORMIX DB 中没有 ERROR: Could not SQLExecute 我找到了解决方案(您可以查看我的答案)。非常感谢您的帮助!【参考方案2】:

这可能与您正在使用的服务器版本有关(不太可能,但可能),或者与您正在使用的 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
$

如您所见,func()proc() 在 ESQL/C 中都正确报告了错误。所以,问题几乎肯定出在客户端代码中——在 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 错误的数据包,那么您就知道问题出在客户端处理错误的方式上。如果您没有看到这两个错误,那么我很想知道发生了什么。

【讨论】:

嗯,不错。你是对的。这有效(导出变量,通过isql 执行然后sqliprint xyz),我收到相同的错误(-648)。此外,如果我直接从 INFORMIX 服务器使用dbaccess 执行调用,我会再次收到错误消息。问题是,isql(与我的简单C 应用程序相同)没有显示任何错误,而是返回空字符串(func)。那很奇怪。这让我觉得,这可能与配置有关,或者我不知道。也许odbc.iniodbcinst.inisqlhosts 中有一些额外的选项,或者我不知道:\ 哈!好的!我知道这个空字符串(返回的)来自哪里......这是错误描述。或者类似的东西。我的意思是,当我使用isql 执行调用时,返回空字符串,没有检测到错误。但是当我用-v(详细)开始isql时,再次检测到NO错误(成功执行),BUT空字符串不再是空字符串,它是设置为Cannot open DEBUG file for SPL routine trace,这是预期的。问题是,执行仍然很好并且没有显示错误(只是错误消息) 我不知道unixODBC 是否有这样的选项(设置详细),但即使有,如果没有返回错误代码,我不能依赖错误消息:\ I '将继续我的研究,我们有一些进展(小,不幸的是,但进展)。欢迎任何想法:) 谢谢! 我拥有的 CSDK 是 clientsdk.3.70.UC2DE.LINUX.tar,unixODBC 版本是 2.3.0 - 都是最新版本。您是否安装了isql(如果我没记错的话,它是 CSDK 自带的)?如果是这样,请您在 isql 中运行调用吗?顺便说一句,我将finderr 用于-648,它被发现、描述等(在客户端和服务器机器上)所以,这不是什么神秘的错误.. 我找到了解决方案(您可以查看我的答案)。非常感谢您的帮助!【参考方案3】:

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


总结:

SQLPrepareSQLExecutesome 错误中有时失败,但不是全部。当使用存储 procedure 时,这些函数会捕获更多错误。不幸的是,存储函数的情况有所不同。

我现在如何捕捉错误?如果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 )

它说 - 如果有 返回列 并且在第一次调用时 fetch 失败,那么就有问题了。然后我有

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

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

【讨论】:

如果你说的是真的,那么我认为这是驱动程序中的一个错误。 SQLExecute 应该失败 SQL_SUCCEEDED。 DBD::ODBC 可能会显示错误,因为无论 SQL_SUCCEEDED(SQLExecute()) 是否为真,它都会在 SQLExecute 之后调用 SQLError。代码中的注释说“无论 rc 的值如何,都调用 dbd_error,这样我们就可以获得所需的任何状态消息。”。我不记得我是否写了那段 DBD::ODBC。你可以在cpansearch.perl.org/src/MJEVANS/DBD-ODBC-1.31/dbdimp.c找到它 是的,我猜这是驱动程序错误。或者我做错了什么:) 我明白了,谢谢你的参考。我现在做这样的事情,如dbdimp.c - “检查以防万一”。问题是,我不确定这是否总是有效,或者有时我会收到一些“误报”错误。但无论如何,重要的是,它现在有效。谢谢,不胜感激。

以上是关于在存储的**过程**中检测到相同的错误,但在存储的**函数**中未检测到的主要内容,如果未能解决你的问题,请参考以下文章

如何使用多个输出参数执行存储过程?

存储过程如何在从 BIDS 调用时不返回行,但在使用相同参数时从 SSMS 调用时返回行?

调用 MySQL 存储过程时出现数据包乱序错误

Spring Boot:在长时间运行的存储过程中检测到明显的连接泄漏

在存储过程中执行时选择查询不检索记录

SQL Server 存储过程在查询分析器中执行,但在 C# 中无法正常运行