在本节中,我将添加一些简单的错误处理函数,并向您展示如何正确释放您创建的句柄资源。为了使您的 ODBC 编程更轻松一些,您可以使用以下函数来检查成功或失败:
static bool SQL_OK(SQLRETURN result){
if(result==SQL_SUCCESS || result==SQL_SUCCESS_WITH_INFO)
return(TRUE);
else
return(FALSE);
}
对SQL_OK()
的典型调用可能如下所示:
if( SQL_OK( SQLAllocHandle( SQL_HANDLE_ENV, SQL_NULL_HANDLE, &handle ))
{
...
}
那么SQL_SUCCESS
和SQL_SUCCESS_WITH_INFO
之间有什么区别?简单的答案是SQL_SUCCESS
意味着函数成功;
SQL_SUCCESS_WITH_INFO
也意味着函数成功,但有更多信息可用。例如,如果您尝试 REVOKE
用户的权限,但该用户一开始就没有该权
限,您将得到SQL_SUCCESS_WITH_INFO
结果。请求已成功完成,但您可能想了解额外的信息。
在 ODBC 2.x 应用程序中,您可以调用 SQLError()
来检索任何扩展返回信息。如果在收到SQL_SUCCESS
结果后调用SQLError()
,则SQLError()
函数将失败。以下是SQLError()
函数的函数原型:
SQLRETURN SQLError(
SQLHENV envHandle,
SQLHDBC conHandle,
SQLHSTMT stmtHandle,
SQLCHAR *sqlState,
SQLINTEGER *nativeError,
SQLCHAR *messageText,
SQLSMALLINT messageTextLength,
SQLSMALLINT *requiredLength
);
请注意,SQLError()
函数可以接受三个不同的句柄,当您调用SQLError()
时,您仅提供这三个句柄之一。例如,如果您在语句句柄上收到错误状态,则可以调用SQLError()
,如下所示:
SQLError( SQL_NULL_HENV, SQL_NULL_HDBC, stmtHandle, ... );
表2 显示了在给定每种句柄类型的情况下如何调用SQLError()
。
句柄类型 | SQLError() 参数 |
---|---|
SQLHENV |
henv 、SQL_NULL_HDBC 、SQL_NULL_HSTMT 、... |
SQLHDBC |
SQL_NULL_HENV 、hdbc 、SQL_NULL_HSTMT 、... |
SQLHSTMT |
SQL_NULL_HENV 、SQL_NULL_HDBC 、stmt 、... |
如果SQLError()
函数成功[1],它将返回三条状态信息。
[1]如果您给
SQLError()
提供了错误的句柄或者没有更多消息可报告给应用程序,则 SQLError() 将失败。
第一个称为SQLSTATE
。sqlState
参数应指向6字节SQLCHAR
数组。SQLError()
将使用五个字符的代码(和 NULL 终止符)填充sqlState
数组。ODBC 使用SQLSTATE
作为一种以独立于数据库的格式提供状态信息的方法。SQLSTATE
代码由两个字符的类和后跟的三个字符的子类组成。SQLSTATE
代码“ 00000
”表示“成功完成”,相当于SQL_SUCCESS
。以类“01”开头的SQLSTATE
值是警告。任何其他SQLSTATE
类都指示错误。表 3 显示了一些常见的SQLSTATE
值。
SQL状态 | 意义 |
---|---|
00000 |
顺利完成 |
01004 |
警告字符串数据,右截断(即,您尝试将 20 个字节选择到 10 字节缓冲区中) |
23000 |
违反完整性约束(例如,您尝试将重复的键值添加到唯一索引中) |
42000 |
语法错误或违反访问规则 |
HY010 |
功能顺序错误 |
42S02 |
未找到基表(或视图) |
SQLError()
返回的第二条信息是本机错误号。驱动程序返回本机错误号,您必须先知道应用程序连接到哪种数据库,然后才能理解本机错误号。并非所有驱动程序都会返回本机错误号。
SQLError()
返回的最有用的信息是错误消息的文本。SQLError()
的最后三个参数用于检索错误消息。messageText
参数指向SQLCHAR
数组。该数组的长度应为SQL_MAX_MESSAGE_LENGTH+1
个字节。messageTextLength
告诉SQLError()
它可以写入*messageText
的字节数。SQLError()
将包含消息文本所需的字节数写入requiredLength
[2]参数指向的SQLSMALLINT
中。
[2]许多API函数需要返回可变长度的信息?不知何故,调用者必须知道为返回信息分配多少空间。此问题的常见解决方案是调用一个函数两次。当您进行第一次调用时,您告诉函数您为可变长度信息分配了 0 个字节。该函数通过设置类似于前面描述的
requiredLength
参数来告诉您需要多少空间。知道需要多少空间后,分配所需的字节数并再次调用该函数。在SQLError()
的情况下,requiredLength
参数毫无意义。对于每个诊断,我们不能多次调用SQLError()
,因为一旦SQLError()
从给定句柄检索诊断,该诊断就会被丢弃。
示例2 client2.c
,展示了包含一些错误处理的代码
#include <sql.h>
#include <sqlext.h>
#include <sqltypes.h>
#include <stdio.h>
#include <stdlib.h>
typedef enum {FALSE, TRUE} bool;
static bool SQL_OK(SQLRETURN result){
if(result==SQL_SUCCESS || result==SQL_SUCCESS_WITH_INFO)
return(TRUE);
else
return(FALSE);
}
您已经了解过SQL_OK()
函数?它只是检查 ODBC 返回的两个成功代码。
static bool printErrors(SQLHENV envHandle,
SQLHDBC conHandle,
SQLHSTMT stmtHandle){
SQLRETURN result;
SQLCHAR sqlState[6];
SQLINTEGER nativeError;
SQLSMALLINT requiredLength;
SQLCHAR messageText[SQL_MAX_MESSAGE_LENGTH+1];
do{
result = SQLError(envHandle,
conHandle,
stmtHandle,
sqlState,
&nativeError,
messageText,
sizeof(messageText),
&requiredLength);
if(SQL_OK(result)){
printf("SQLState = %s\n", sqlState);
printf("Native error = %d\n", nativeError);
printf("Message text = %s\n", messageText);
}
} while(SQL_OK(result));
}
printErrors()
函数是新函数。您调用SQLError()
直到它返回失败代码。为什么要多次调用SQLError()
?因为每个 ODBC 函数都可能返回多个错误。请记住,每次SQLError()
成功返回时,它都会从给定句柄中删除单个诊断。如果您不从句柄中检索所有错误,它们将在您下次使用该句柄时被丢弃。
int main(int argc, char * argv[]){
SQLRETURN res;
SQLHENV henv;
SQLHDBC conn;
SQLCHAR fullConnectStr[SQL_MAX_OPTION_STRING_LENGTH];
SQLSMALLINT requiredLength;
// 1.申请环境句柄
res = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);
if(SQL_OK(res)){
// 2.设置环境属性
res = SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC2, 0);
if(!SQL_OK(res)){
printErrors(henv, SQL_NULL_HDBC, SQL_NULL_HSTMT);
exit(-1);
}
// 3.申请连接句柄
res = SQLAllocHandle(SQL_HANDLE_DBC, henv, &conn);
if(!SQL_OK(res)){
printErrors(henv, SQL_NULL_HDBC, SQL_NULL_HSTMT);
exit(-2);
}
res = SQLConnect(conn, // connection handle
argv[1], SQL_NTS, // data source name
argv[2], SQL_NTS, // user name
argv[3], SQL_NTS); // password
if(!SQL_OK(res)){
printErrors(SQL_NULL_HENV, conn, SQL_NULL_HSTMT);
exit(-3);
}
printf("connection ok...disconnecting\n");
res = SQLDisconnect(conn);
if(!SQL_OK(res)){
printErrors(SQL_NULL_HENV, conn, SQL_NULL_HSTMT);
exit(-4);
}
res = SQLFreeHandle(SQL_HANDLE_DBC, conn);
if(!SQL_OK(res)){
printErrors(SQL_NULL_HENV, conn, SQL_NULL_HSTMT);
exit(-5);
}
res = SQLFreeHandle(SQL_HANDLE_ENV, henv);
if(!SQL_OK(res)){
printErrors(henv, SQL_NULL_HDBC, SQL_NULL_HSTMT);
exit(-6);
}
}
exit(0);
}
main()
函数中有三个新功能。
首先,您会注意到我在代码中乱七八糟地调用了printErrors()
。每当 ODBC 函数返回失败状态时,您都可以调用printErrors()
。当您获得SQL_SUCCESS_WITH_INFO
状态时,您也可以调用printErrors()
,但在大多数情况下,额外的信息是无用的。
请注意,一旦遇到错误,您就会退出。每次调用exit()
都会指定不同的值:如果程序成功,则返回0
;在所有其他情况下,您将返回一个唯一的负数。返回值被提供给调用程序(通常是 shell)并用于检查成功或失败。
我要解释的关于这个客户端的最后一件事是拆卸代码。要正确清理客户端应用程序,您必须断开连接句柄(使用SQLDisconnect()
),然后使用SQLFreeHandle()
释放连接和环境句柄。拆除连接的顺序很重要。在断开连接之前,您将无法释放连接句柄。在所有连接句柄断开并释放之前,您将无法释放环境句柄。
如果要运行此程序,执行如下:
gcc -o client2 client2.c -I /home/postgres/odbc/include -L /home/postgres/odbc/lib -lodbc
./client2 PSQLODBC postgres postgresql@123
现在您知道如何连接到数据库、如何检测错误以及如何正确断开 ODBC 连接。
标签:res,句柄,odbc,添加,SQLError,SQL,OK,NULL,客户端 From: https://www.cnblogs.com/jl1771/p/17986055