Các chẩn đoán và xử lý lỗi
Mỗi khi một hàm CLI/ODBC được gọi, một giá trị đặc biệt được gọi là một mã trả về được trả về cho ứng dụng gọi hàm đó để biểu thị hàm đã thi hành như mong đợi hay không. Nếu, vì một vài lý do, hàm này đã không thi hành thành công, giá trị của mã trả về đã sinh ra sẽ biểu thị nguyên nhân làm cho hàm không thực hiện thành công. Một danh sách các mã có khả năng có thể được trả về bởi một hàm CLI/ODBC có thể xem trong Bảng 2:
Bảng 2. Các mã trả về của hàm CLI/ODBC
| Mã trả về | Ý nghĩa |
SQL_SUCCESS
| Hàm CLI/ODBC thực hiện thành công. |
SQL_SUCCESS_WITH_INFO
| Hàm CLI/ODBC thực hiện thành công, tuy nhiên, đã gặp phải một điều kiện cảnh báo hay điều kiện lỗi không trầm trọng. |
SQL_NO_DATA or SQL_NO_DATA_FOUND
| Hàm CLI/ODBC thực hiện thành công, nhưng không tìm thấy có dữ liệu có liên quan. |
SQL_INVALID_HANDLE
| Hàm CLI/ODBC không được thực hiện thành công, vì đã cho một thẻ điều khiển môi trường, kết nối, câu lệnh, hoặc bộ mô tả không hợp lệ. Mã trả về này chỉ được trả về khi thẻ điều khiển đã cho hoặc chưa được cấp phát hoặc kiểu thẻ điều khiển sai (ví dụ, nếu cung cấp một thẻ điều khiển kết nối khi mong đợi là một thẻ điều khiển môi trường). Bởi kiểu lỗi này là một lỗi lập trình, không có thông tin bổ sung nào được cung cấp. |
SQL_NEED_DATA
| Hàm CLI/ODBC không thực hiện thành công bởi vì dữ liệu mà hàm
này mong đợi có sẵn vào thời gian thi hành (ví dụ như dữ liệu
các dấu tham số hoặc thông tin kết nối) đã bị thiếu. Mã trả về
này thường được tạo ra khi các tham số hoặc cột được kết buộc
như là các tham số/các cột dữ liệu-lúc-thi hành (SQL_DATA_AT_EXEC). |
SQL_STILL_EXECUTING
| Một hàm CLI/ODBC đã được bắt đầu không đồng bộ vẫn còn đang thực hiện. |
SQL_ERROR
| Hàm CLI/ODBC không thực hiện thành công. |
Việc xử lý lỗi là một phần quan trọng của bất kỳ ứng dụng nào và các ứng dụng CLI/ODBC không là ngoại lệ. Ít nhất, một ứng dụng CLI/ODBC sẽ luôn luôn kiểm tra để xem xem một hàm CLI/ODBC có thực hiện thành công không bằng cách kiểm tra lại mã trả về đã sinh ra. Bất cứ khi nào một hàm không thực hiện thành công như mong đợi, những người dùng sẽ được thông báo rằng một điều kiện lỗi hay cảnh báo đã xuất hiện và mỗi khi có thể, họ sẽ được cung cấp các thông tin chẩn đoán để giúp họ nhanh chóng xác định vị trí và sửa chữa lại tình hình.
Mặc dù một mã trả về thông báo cho một chương trình ứng dụng rằng đã
bắt gặp một điều kiện lỗi hay cảnh báo, nhưng nó không cung cấp cho
ứng dụng (hoặc nhà phát triển, hoặc người dùng) thông tin cụ thể về
nguyên nhân gây ra điều kiện lỗi hay cảnh báo đã xuất hiện. Bởi vì các
thông tin bổ sung thêm về một điều kiện lỗi hay cảnh báo thường là cần
thiết để giải quyết vấn đề, DB2 (giống như các sản phẩm cơ sở dữ liệu
quan hệ khác) sử dụng một bộ các mã thông báo lỗi được gọi là các
SQLSTATE để cung cấp các thông tin chẩn đoán bổ sung cho các cảnh báo
và các lỗi. Các SQLSTATE là các chuỗi vừa có chữ vừa có số có năm ký
tự (các byte) theo chiều dài và có định dạng ccsss, ở đây
cc chỉ thị lớp thông báo lỗi và sss chỉ thị lớp con
thông báo lỗi. Bất kỳ SQLSTATE nào có một lớp là 01 tương ứng với một cảnh báo; Bất kỳ SQLSTATE nào có một
lớp là HY tương ứng với một lỗi do DB2 CLI
tạo ra và bất kỳ SQLSTATE nào có một lớp IM
tương ứng với một lỗi do Trình quản lý của trình điều khiển ODBC tạo
ra. (Bởi vì các máy chủ cơ sở dữ liệu khác nhau thường có các mã thông
báo chẩn đoán khác nhau, các SQLSTATE tuân theo các tiêu chuẩn được
nêu trong đặc tả kỹ thuật tiêu chuẩn X/Open CLI. Việc tiêu chuẩn hóa
các giá trị SQLSTATE này cho phép các nhà phát triển ứng dụng xử lý
các lỗi và các cảnh báo một cách nhất quán trên các sản phẩm cơ sở dữ
liệu quan hệ khác nhau).
Không giống như mã trả về, các SQLSTATE thường được xử lý như các hướng dẫn và các trình điều khiển không bắt buộc phải trả về chúng. Vì vậy, trong khi các trình điều khiển nên luôn luôn trả về SQLSTATE đúng đối với bất kỳ lỗi hay cảnh báo nào mà chúng có khả năng phát hiện, các ứng dụng không nên trông mong rằng điều đó luôn luôn xảy ra. Do các SQLSTATE không chắc chắn sẽ được trả về, nên hầu hết các ứng dụng chỉ hiển thị chúng cho người sử dụng cùng với bất kỳ thông báo chẩn đoán tương ứng nào và mã lỗi nguyên sinh sẵn có.
Vậy thì làm thế nào để nhận được các giá trị SQLSTATE, các thông báo
chẩn đoán và các mã lỗi nguyên sinh khi một hàm CLI/ODBC không thi
hành đúng? Thông tin này được thu thập bằng cách gọi hàm SQLGetDiagRec(), hàm SQLGetDiagField() hoặc cả hai. Các hàm này nhận đầu vào là
một thẻ điều khiển môi trường, thẻ điều khiển kết nối, thẻ điều khiển
câu lệnh, hoặc thẻ điều khiển bộ mô tả và trả về các thông tin chẩn
đoán về hàm CLI/ODBC cuối cùng vừa được thi hành, sử dụng thẻ điều
khiển đã chỉ rõ. Nếu nhiều bản ghi chẩn đoán đã được sinh ra, một ứng
dụng phải gọi một hoặc cả hai hàm này lặp lại nhiều lần cho đến khi
tất cả các thông tin chẩn đoán có sẵn được thu nhận. (Tổng số các bản
ghi chẩn đoán có sẵn có thể được xác định bằng cách gọi hàm SQLGetDiagField() với số hiệu bản ghi là
0 -- đó là số hiệu của bản ghi tiêu đề
-- và chỉ rõ tùy chọn SQL_DIAG_NUMBER).
Các thông tin chẩn đoán được lưu trong bộ nhớ như là các bản ghi chẩn
đoán. Các ứng dụng có thể lấy ra các giá trị SQLSTATE, các thông báo
chẩn đoán và các mã lỗi nguyên sinh từ một bản ghi chẩn đoán trong chỉ
một bước bằng cách gọi hàm SQLGetDiagRec().
Tuy nhiên, hàm này không thể được sử dụng để lấy ra thông tin từ bản
ghi tiêu đề chẩn đoán. Thay vào đó, các ứng dụng phải sử dụng hàm
SQLGetDiagField() để lấy ra các thông
tin được lưu trữ trong bản ghi tiêu đề chẩn đoán. Hàm SQLGetDiagField() cũng có thể được sử dụng để
thu nhận được các giá trị của các trường trong từng bản ghi chẩn đoán
riêng lẻ. (Ví dụ, hàm SQLGetDiagField() có
thể được sử dụng để thu nhận thông tin về số hàng bị ảnh hưởng bởi một
hoạt động chọn, chèn, cập nhật hoặc xóa).
Bạn đã thấy các mã trả về có thể được sử dụng như thế nào để phát hiện
khi một điều kiện lỗi hay cảnh báo xuất hiện và các bản ghi chẩn đoán
có thể được sử dụng như thế nào để cung cấp các thông tin phản hồi.
Bây giờ bạn sẽ thấy việc lấy ra các thông tin chẩn đoán và xử lý lỗi
thường được thực hiện trong một ứng dụng CLI/ODBC như thế nào. Ứng
dụng CLI/ODBC dưới đây, được viết bằng ngôn ngữ lập trình C, minh họa
cách các hàm SQLGetDiagRec() và SQLGetDiagField() có thể được sử dụng để hiển
thị các thông tin chẩn đoán như thế nào, cả cho trường hợp khi một lỗi
xảy ra lẫn khi cần nhiều thông tin hơn về việc xử lý kết hợp với một
câu lệnh SQL SELECT.
Liệt kê 4. Sử dụng các hàm SQLGetDiagField() và SQLGetDiagRec()
#include <stdio.h>
#include <string.h>
#include <sqlcli1.h>
int main()
{
// Declare The Local Memory Variables
SQLHANDLE EnvHandle = 0;
SQLHANDLE ConHandle = 0;
SQLHANDLE StmtHandle = 0;
SQLCHAR ConString[512];
SQLRETURN RetCode = SQL_SUCCESS;
SQLSMALLINT Counter = 0;
SQLINTEGER NumRecords = 0;
SQLINTEGER NativeErr = 0;
SQLCHAR SQLState[6];
SQLCHAR ErrMsg[255];
SQLSMALLINT ErrMsgLen = 0;
SQLCHAR SQLStmt[255];
// Allocate An Environment Handle
SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE,
&EnvHandle);
// Set The ODBC Application Version To 3.x
if (EnvHandle != 0)
SQLSetEnvAttr(EnvHandle, SQL_ATTR_ODBC_VERSION,
(SQLPOINTER) SQL_OV_ODBC3, SQL_IS_UINTEGER);
// Allocate A Connection Handle
if (EnvHandle != 0)
SQLAllocHandle(SQL_HANDLE_DBC, EnvHandle,
&ConHandle);
// Build A Valid Connection String
sprintf((char *) ConString, "driver={IBM DB2 ODBC DRIVER};");
strcat((char *) ConString, "dbalias=sample;uid=db2admin;pwd=ibmdb2");
// Connect To The Appropriate Data Source
if (ConHandle != 0)
RetCode = SQLDriverConnect(ConHandle, NULL, (SQLCHAR *) ConString,
SQL_NTS, NULL, 0, NULL,
SQL_DRIVER_NOPROMPT);
// If Unable To Establish A Data Source Connection,
// Obtain Any Diagnostic Information Available
if (RetCode != SQL_SUCCESS)
{
// Find Out How Many Diagnostic Records Are
// Available
SQLGetDiagField(SQL_HANDLE_DBC, ConHandle, 0,
SQL_DIAG_NUMBER, &NumRecords, SQL_IS_INTEGER,
NULL);
// Retrieve And Display The Diagnostic Information
// Produced
for (Counter = 1; Counter <= NumRecords; Counter++)
{
// Retrieve The Information Stored In Each
// Diagnostic Record Generated
SQLGetDiagRec(SQL_HANDLE_DBC, ConHandle, Counter,
SQLState, &NativeErr, ErrMsg, sizeof(ErrMsg),
&ErrMsgLen);
// Display The Information Retrieved
printf("SQLSTATE : %s\n", SQLState);
printf("%s\n", ErrMsg);
// Prepare To Exit
goto EXIT;
}
}
// Allocate An SQL Statement Handle
if (ConHandle != 0)
SQLAllocHandle(SQL_HANDLE_STMT, ConHandle,
&StmtHandle);
// Set The Appropriate Statement Attributes
SQLSetStmtAttr(StmtHandle, SQL_ATTR_DEFERRED_PREPARE,
(SQLPOINTER) SQL_DEFERRED_PREPARE_OFF, 0);
SQLSetStmtAttr(StmtHandle, SQL_ATTR_CURSOR_TYPE,
(SQLPOINTER) SQL_CURSOR_STATIC, 0);
// Define A SELECT SQL Statement
strcpy((char *) SQLStmt, "SELECT deptno, deptname FROM ");
strcat((char *) SQLStmt, "department");
// Prepare The SQL Statement
if (SQLPrepare(StmtHandle, (SQLCHAR *) SQLStmt, SQL_NTS) == SQL_SUCCESS)
{
// Obtain The Estimated Number Of Rows That Will Be
// Returned By The SELECT Statement
RetCode = SQLGetDiagField(SQL_HANDLE_STMT, StmtHandle, 0,
SQL_DIAG_CURSOR_ROW_COUNT, &NumRecords,
SQL_IS_INTEGER, NULL);
// Display The Information Retrieved
if (RetCode == SQL_SUCCESS)
printf("Estimated number of records that will be returned : %d\n",
NumRecords);
// Execute The SELECT SQL Statement
SQLExecute(StmtHandle);
// Obtain The Actual Number Of Rows Returned By
// The SELECT Statement
RetCode = SQLGetDiagField(SQL_HANDLE_STMT, StmtHandle, 0,
SQL_DIAG_CURSOR_ROW_COUNT, &NumRecords,
SQL_IS_INTEGER, NULL);
// Display The Information Retrieved
if (RetCode == SQL_SUCCESS)
printf("Actual number of records returned : %d\n",
NumRecords);
}
EXIT:
// Free The SQL Statement Handle
if (StmtHandle != 0)
SQLFreeHandle(SQL_HANDLE_STMT, StmtHandle);
// Free The Connection Handle
if (ConHandle != 0)
SQLFreeHandle(SQL_HANDLE_DBC, ConHandle);
// Free The Environment Handle
if (EnvHandle != 0)
SQLFreeHandle(SQL_HANDLE_ENV, EnvHandle);
// Return Control To The Operating System
return(0);
}
|