人大金仓数据库KingbaseES DCI中的错误码处理流程

DCI中的错误码处理流程

关键字:

错误码、ODBC、DCI、人大金仓、KingbaseES

DCI层错误码

错误码是一组数字(或者数字与字母的组合),会与各种错误消息建立关联,用于识别应用程序运行过程产生的各种问题,便于程序员快速定位和解决问题。从程序员的角度来说,错误码是程序中出现问题的指示器;从用户角度出发,错误码是软件和操作系统中的常见问题;从组织的角度看,错误码也是重要的质量控制指标。因此,一套完善的错误码处理流程是应用程序必须具备的功能。

为保持与Oracle的高度兼容,DCI层需要设置Oracle对应的错误码,这些错误码主要记录DCI接口函数在执行过程中的错误码和ODBC层传输到DCI层的错误码。DCI层的错误码主要定义在error.h头文件中,使用最多的是Oracle对应的错误码,各个错误码的定义、错误码值、错误消息与设置方法如表1所示。

表1 DCI层错误码(部分)

定义

错误码

错误消息

设置方法

ORA_00001

1

kdci.c:duplicate key violates primary key constraint
duplicate key violates unique constraint
duplicate key violates unique index

 

ORA_00384

384

kdci_lo.c:Insufficient memory to grow cache

InternalSetError

ORA_00942

942

kdci.c:relation "T" does not exist

 

ORA_01014

1014

kdci_stmt.c:
kdci.c:
KingbaseES is shutting down

 

ORA_01019

1019

kdci_stmt.c:unable to allocate memory in the userside
illegal sql statement

InternalSetError

ORA_01034

1034

kdci_stmt.c:
kdci.c:
Could not connect to the server
kdci_describe.c:KSAPI_ProcedureColumns_Package query error.
SQLProcedureColumns query error.
Could not fetch.
Could not connect to the server.

InternalSetError

ORA_01036

1036

kdci_stmt.c:illegal variable name/number

InternalSetError

ORA_01092

1092

kdci_stmt.c:
kdci.c:
Communication link failure

 

ORA_01653

1653

kdci.c:could not extend relation

 

ORA_01841

1841

kdci_describe.c:(full) year must be between -4713 and +9999, and not be 0

InternalSetError

ORA_01843

1843

kdci_describe.c:An invalid month was specified

InternalSetError

ORA_01846

1846

kdci_describe.c:not a valid day of the week

InternalSetError

ORA_01847

1847

kdci_describe.c:day of month must be between 1 and last day of month

InternalSetError

ORA_01850

1850

kdci_describe.c:hour must be between 0 and 23

InternalSetError

ORA_01851

1851

kdci_describe.c:minutes must be between 0 and 59

InternalSetError

ORA_01852

1852

kdci_describe.c:seconds must be between 0 and 59

InternalSetError

DCI层错误码主要存储在DCIError句柄中。该句柄的定义如下所示:

struct DCIError

{

DCIHandleHead head; // head type=DCI_HTYPE_ERROR,结构体的第一个

int allocated_count;//可以分配的错误数量,MAX_ERROR_SIZE(16)

int err_count;//当前的错误数量

KSQLSMALLINT *handletype;//错误类型,包含KSQL_HANDLE_DBC、KSQL_HANDLE_STMT、DCI_HTYPE_DIRPATH_CTX,0等

void **handle;//发生错误的句柄

int *__error_number;//错误码

char **__error_message;//错误消息

char **sqlstate;//ODBC层错误代码

DCIEnv *pEnv;//关联的环境句柄,只在申请时设置

};

当handletype为KSQL_HANDLE_DBC、KSQL_HANDLE_STMT时,该错误码为ODBC层的错误码。一个环境句柄只能有一个错误句柄,最多可存储16条错误信息,每一条错误信息包含:错误类型、发生错误的句柄(可为NULL)、错误码、错误消息、sqlstate。

图1 DCI层错误码日志记录

ODBC层错误码

由于DCI底层复用ODBC的代码,因此DCI的错误码类型较多,不仅包含了DCI层与Oracle兼容的错误码,还包括了ODBC层DescriptorClass、StatementClass、ConnectionClass、EnvironmentClass四类句柄的错误码。其对应的定义、错误码值和错误消息如表2所示。

表2 ODBC错误码(部分)

定义

错误码

ODBC错误码

错误消息

设置方法

LOWEST_STMT_ERROR

-6

STMT_ERROR_IN_ROW

-6

01S01

01S01

convert.c:conversion error to wide chars occurred

SC_set_error

STMT_ERROR_OPTION_VALUE_CHANGED

-5

01S02

01S02

execute.c:cursor updatability changed
options.c:Requested value changed.

SC_set_error

STMT_ERROR_ROW_VERSION_CHANGED

-4

01001

01001

results.c:the row was already deleted ?
the content was deleted after last fetch
the driver cound't identify inserted rows

SC_set_error

STMT_ERROR_POS_BEFORE_RECORDSET

-3

01S06

01S06

results.c:fetch prior from eof and before the beginning
fetch last and before the beginning
fetch absolute and before the beginning
fetch relative and before the beginning

SC_set_error

STMT_ERROR_TRUNCATED

-2

01004

01004

results.c:The buffer was too small for the colName.
The buffer was too small for the GetData.
The buffer was too small for the GetCursorName.
statement.c:Fetched item was truncated.

SC_set_error

在ODBC层,其错误码主要存储在各个句柄定义的错误消息相关成员中:

//仅列出与错误相关的参数,其余参数不做展示

struct EnvironmentClass_

{

int errornumber;

char* errormsg;

};

struct ConnectionClass_

{

char* __error_message;

int __error_number;

char sqlstate[8];

};

struct StatementClass_

{

char *__error_message;//语句执行过程中的错误信息

int __error_number;//语句执行过程中的错误码

KS_ErrorInfo *kserror;//记录__error_number对应的错误信息,并进行相应处理,以便返回给上层应用

};

typedef struct DescriptorHeader_

{

UInt4 error_row; /* 1-based row */

UInt4 error_index; /* 1-based index */

Int4 __error_number;

char* __error_message;

KS_ErrorInfo* kserror;// 记录__error_number对应的错误信息,并进行相应处理,以便返回给上层应用

DescriptorHeader;

typedef struct

{

UInt4 status;//错误码,连接错误码或者语句错误码

Int2 errorsize;//错误信息的长度sizeof(__error_message)

Int2 recsize;//-1

Int2 errorpos;

char sqlstate[6];

KSQLLEN diag_row_count;

char __error_message[40];//错误消息

KS_ErrorInfo;

DCI与ODBC层错误码设置

(1)DCI层错误码设置

DCI层错误码设置主要通过InternalSetError( )函数实现,操作对象为DCIError,该函数的定义如下:

void InternalSetError(DCIError *errhp, 

int handle_type, 

void *handle, 

int error_code, 

char *msg, 

char *sqlstate)

函数主要包含三步操作,错误码判断,错误句柄空间申请和错误信息设置:

  1. 错误码判断主要是对ODBC层传输的错误码进行转换,具体方式在下一节详述;
  2. 错误句柄空间申请。在错误句柄DCIError初始化时,仅申请MAX_ERROR_SIZE大小的空间,若是在保存新的错误信息时,空间不足,需要扩充错误句柄空间,并更新相关参数,如图2所示。

C:\Users\yangli\Desktop\OCI接口函数调用\错误句柄空间申请与错误码设置.png

图2 错误句柄空间申请实现

  1. 错误消息设置主要是将对应的句柄、错误码、sqlstate和错误类型写入错误句柄,并更新相关参数。

C:\Users\yangli\Desktop\OCI接口函数调用\错误消息设置.png

图3 错误消息设置实现

(2)ODBC错误码设置

在ODBC层,各个句柄的错误码是分开设置的:

  1. 对于EnvironmentClass中发生的错误,只记录一条错误信息,一旦发生错误,整个流程结束,因此直接在句柄操作过程中设置,并没有专用的错误设置函数。
  2. 对于ConnectionClass中发生的错误,调用CC_set_error( )与CC_set_errormsg( )进行设置,一个句柄也仅能存储一条错误信息。
  3. 对于StatementClass中发生的错误,调用SC_set_error( )与SC_set_errormsg( )处理,一个句柄对应一条错误信息,但该错误在设置时通过ref_CC_error参数与ConnectionClass句柄联系。
  4. 对于DescriptorClass中发生的错误,调用DC_set_error( )与DC_set_errormsg( )进行设置。一个句柄仅能存储一条错误码。

ODBC层错误码获取与转换

为了将底层错误码传到DCI层进行转换和处理,DCI设计了ODBC层的错误码获取与转换机制,大致思路如图4所示。

C:\Users\yangli\Desktop\OCI接口函数调用\错误信息获取KSAPI_GetDiagRec.png

图4 ODBC层错误码处理与转换

针对不同的错误码类型,设计了不同的错误码获取方法和思路:

(1)KSQL_HANDLE_ENV

环境句柄的错误消息通过KSAPI_EnvError( )进行获取,实现思路为:

  1. RecNumber判断,为1或者-1才可进行下一步,设置为1。若不满足条件,返回KSQL_NO_DATA_FOUND;
  2. cbErrorMsgMax判断,大于0才可进行下一步。若不满足条件,返回KSQL_ERROR;
  3. 调用ENV_get_error判断当前环境创建是否存在错误,有返回1,没有为0。若没有错误或者错误消息为NULL;

①调用strncpy_null将szSqlState设置为‘00000’,设置传入的参数,返回KSQL_NO_DATA_FOUND。

  1. 判断并设置pcbErrorMsg、错误消息szErrorMsg && (cbErrorMsgMax > 0)、错误码pfNativeError参数的状态;
  2. 如果szSqlState(存放5位ODBC错误码)不为空,根据当前环境对象的错误码status调用ks_sqlstate_set与ODBC错误码的映射:ENV_ALLOC_ERROR==>HY001 or S1001,default==>HY000 or S1000;
  3. 返回KSQL_SUCCESS。

涉及到的相关方法如图5所示:

图5 环境句柄错误信息传输路径

(2)KSQL_HANDLE_DBC

连接句柄的错误消息通过KSAPI_ConnectError( )进行获取,实现思路为:

  1. RecNumber判断,为1或者-1才可进行下一步,设置为1。若不满足条件,返回KSQL_NO_DATA_FOUND;
  2. cbErrorMsgMax判断,大于0才可进行下一步。若不满足条件,返回KSQL_ERROR;
  3. 判断当前连接状态,conn->status=CONN_EXECUTING;CC_get_error(conn):有错误为1,没有错误为0;错误消息为空,满足其中之一,则连接正常;

① 调用strncpy_null将szSqlState设置为‘00000’,设置传入的参数,返回KSQL_NO_DATA_FOUND。

  1. 判断并设置pcbErrorMsg、错误消息szErrorMsg && (cbErrorMsgMax > 0)、错误码pfNativeError参数的状态;
  2. 如果szSqlState(存放5位ODBC错误码)不为空,进行ConnectionClass错误码与ODBC错误码的映射:

① 如果当前连接conn->sqlstate[0]不为空,直接赋值给szSqlState;

② 否则根据当前连接的错误码status调用ks_sqlstate_set进行映射,映射规则如图6所示。在确定错误码于ODBC错误码之间的对应关系时,需要根据DCI的版本选择相应的sqlstate。

C:\Users\yangli\Desktop\OCI接口函数调用\否则根据当前连接的错误码status调用ks_sqlstate_set进行映射.png

图6 连接句柄错误码映射关系

  1. 如果once_again为真:调用CC_set_errornumber,返回KSQL_SUCCESS_WITH_INFO;否则返回KSQL_SUCCESS。

涉及到的相关方法如图7所示:

图7 连接句柄错误信息传输路径

(3)KSQL_HANDLE_STMT

语句句柄的错误消息通过KSAPI_StmtError ( )进行获取,实现思路为:

  1. 调用SC_get_errornumber获取当前StatementClass对象的错误码errnum;
  2. 调用 SC_create_errorinfo(stmt, &error)获取当前StatementClass对象的错误成员kserror,若是kserror为空,返回KSQL_NO_DATA_FOUND;

① SC_create_errorinfo内部通过使用结构体数组Statement_sqlstate进行STMT错误码与ODBC错误码的映射。

  1. 若是kserror != &error,stmt->kserror = kserror;
  2. 如果错误码errnum为STMT_ERROR_NO_MEMORY且kserror->__error_message[0]为NULL,设置kserror->__error_message为”Memory Allocation Error??”;
  3. 如果应用程序创建的错误信息内存过大,底层驱动程序会自动拆分,因此调用return ER_ReturnError(kserror, RecNumber, szSqlState, pfNativeError, szErrorMsg, cbErrorMsgMax, pcbErrorMsg, flag);来分割错误信息——environ.c;

① 如果kserror为空,返回KSQL_NO_DATA_FOUND;

② 设置kserror->recsize的值;

③ 根据容纳长度设置*pcbErrorMsg;

④ 设置错误码:*pfNativeError = kserror->status;

⑤ 设置SqlState:strncpy_null((char *) szSqlState, kserror->sqlstate, 6)。

  1. 如果写入长度wrtlen小于pcblen,返回KSQL_SUCCESS_WITH_INFO,否则返回KSQL_SUCCESS

涉及到的相关方法如图8所示:

图8 语句句柄错误信息传输路径

(4)KSQL_HANDLE_DESC

描述句柄的错误消息通过KSAPI_DescError ( )进行获取,实现思路为:

  1. 获取DescriptorHeader对象deschd,里面含有错误信息成员kserror;
  2. DescriptorClass对象desc调用DC_create_errorinfo,获取错误信息deschd->kserror;

① DC_create_errorinfo内部根据deschd->__error_number通过使用结构体数组Descriptor_sqlstate进行DESC错误码与ODBC错误码的映射。

  1. 如果应用程序创建的错误信息内存过大,底层驱动程序会自动拆分,因此调用return ER_ReturnError(kserror, RecNumber, szSqlState, pfNativeError, szErrorMsg, cbErrorMsgMax, pcbErrorMsg, flag);来分割错误信息——environ.c;

① 如果kserror为空,返回KSQL_NO_DATA_FOUND

  1. 设置kserror->recsize的值
  2. 根据容纳长度设置*pcbErrorMsg
  3. 设置错误码:*pfNativeError = kserror->status;
  4. 设置SqlState:strncpy_null((char *) szSqlState, kserror->sqlstate, 6);
  5. 如果写入长度wrtlen小于pcblen,返回KSQL_SUCCESS_WITH_INFO,否则返回KSQL_SUCCESS。

涉及到的相关方法如图9所示:

图9 描述句柄错误信息传输路径

错误码优化与改进思路

针对DCI接口错误码设置方式多,参数众多,底层错误码和上层错误码联系不够紧密的问题,初始拟定以下改进思路:

  1. 流程优化:将底层错误码设置、读取和错误日志设置方式进行整合,尽可能用一个方法调用实现所有ODBC层句柄错误码的设置和读取;
  2. 参数优化:摒弃ODBC层DescriptorClass、StatementClass、ConnectionClass的错误参数,将其整合到EnvironmentClass中;
  3. 错误码标准化:对照Oracle错误码手册,将ODBC层错误码的值对应的Oracle数据库错误码的标准输出,进一步提升对Oracle的兼容性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值