金仓数据库KingbaseES——DCI语句准备模块的处理过程
关键字:
DCI、语句准备DCIStmtPrepare、执行DCIStmtExecute、人大金仓、KingbaseES
DCI句柄申请
在完成环境初始化和连接建立之后,就可以开始执行SQL语句。在DCI中,SQL语句需要语句句柄DCIStmt生存。语句句柄的申请和分配在连接建立成功之后进行,对应的接口函数为DCIHandleAlloc( ),该函数内部首先申请DCIStmt占用的空间,申请完成之后,调用 下一层接口KSAPI_AllocStmt( )申请StatementClass需要的空间。
申请语句句柄时,调用语句句柄分配函数:
ret = KSAPI_AllocStmt((HSTMT *) &(((DCIStmt *)(*hndlpp))->hstmt), PKDCI_EXTERNAL_STATEMENT | PKDCI_INHERIT_CONNECT_OPTIONS);
KSAPI_AllocStmt
RETCODE KSQL_API KSAPI_AllocStmt(HSTMT * phstmt, UDWORD flag )
该函数内部调用SC_Constructor( )申请语句句柄类StatementClass占用的内存空间,并对相关参数赋予初值。在此过程中,产生的错误信息由SC_set_error函数在StatementClass的__error_message、__error_number参数上记录,并通过kserror结构传输到上层应用。
DCI语句准备
DCI的语句准备是一个本地操作,不涉及服务端的处理。主要进行的工作包含了了SQL语句格式处理与相关符号定义绑定,并且记录语句内容和参数信息到语句句柄,其主要处理逻辑如图1所示。
图1 初始化模块函数调用关系
由上图可知,首先进行的是参数检查的SQL语句空间申请,还需要将用户给定的SQL语句进行标准化(忽略末尾的空格或者分号)。值得注意的是需要对含有select for update和call proc的语句进行特殊处理:
- 对于“select for update”语句,需要对将该语句对应的句柄的hasForUpdate参数设置为1,添加ctid和xmin参数。
- 对于“call proc”语句,将其重写为"begin call proc...; end;"的形式,方式数据库识别和处理。
DCI执行过程中的语句准备
在DCIStmtPrepare( )函数中的语句准备工作仅仅是一小部分,更充分的准备工作是在DCIStmtExecute( )中调用InnerStmtPrepare( )完成的,主要流程如图2所示。
图2 InnerStmtPrepare( )处理过程
在清理ODBC层语句句柄的结果集和绑定参数时,需要根据是否首次执行,释放相应参数和内存:
(1)如果stmtp->is_dciexecuted > 1,即当前操作并非第一次执行,需要执行SC_free_params(stmtp->hstmt, STMT_FREE_PARAMS_DATA_AT_EXEC_ONLY),释放执行时需要的参数,即设置stmt->data_at_exec = -1;stmt->current_exec_param = -1;stmt->put_data=FALSE这些参数的值;
(2)否则执行SC_free_params(stmtp->hstmt, STMT_FREE_PARAMS_ALL),释放所有参数:STMT_FREE_PARAMS_ALL模式下调用APD_free_params与IPD_free_params释放descriptor handles中的参数和内存,设置stmt->exec_start_row = -1;stmt->exec_end_row = -1;stmt->exec_current_row = -1;stmt->exec_suc_row = -1,并将stmt->data_at_exec、stmt->current_exec_param、stmt->put_data恢复至初始状态。
这一准备过程需要连接服务器进行处理,因此在参数检查和句柄清理阶段的重要工作就是判断连接状态是否可用,若是连接异常,则直接结束语句执行。之后,还需要按照当前服务上下文环境句柄中的字符编码对语句进行编码转换,以便准备识别。此外,若是首次执行,还需要对查询语句进行游标属性的设置:
(1)若为select查询语句,调用KSAPI_SetStmtAttr( )设置游标类型为KSQL_CURSOR_STATIC,内部调用KSAPI_SetStmtOption( );
(2)否则调用KSAPI_SetStmtAttr( )设置为KSQL_CURSOR_TYPE_DEFAULT:KSQL_CURSOR_FORWARD_ONLY,内部调用KSAPI_SetStmtOption( )。
若为首次执行,调用KSAPI_Prepare( )进行更深层次的准备工作,详细内容如图3所示。
图3 KSAPI_Prepare( )处理过程
上述过程执行完成之后,需要将参数更新并记录到ipdflds中,再者还要将ODBC层的参数stmtp->hstmt->is_anonymousblock赋值给匿名块参数stmtp->is_anonymousblock。之后再调用InnerGetParamInfo( )方法获取参数信息,主要涉及的数据结构时DCIStmt中的DCIParam *pParams;参数信息成员和记录参数个数的KSQLSMALLINT cParams;成员。DCIParam的结构如图4所示。
获取参数信息时,首先调用KSAPI_NumParams()计算参数的个数。如果参数个数>0,赋值给stmtp->cParams;释放原有参数数组stmtp->pParams并置空;动态申请参数列表空间,设置stmtp->pParams->ptype = DCI_PTYPE_LIST,为stmtp->pParams->sublist申请空间;进入循环,获取每一个参数的名称、类型、IO模式、数据类型、数据大小、精度、标度等信息并将其插入参数列表中。最后,设置stmtp->param_need_data = 0;stmtp->when_to_need_data = NONE,释放stmtp->col_need_data的内存并置空。
图4 涉及参数信息
至此,OCI中一条SQL语句的准备工作基本完成。