ZLW-NOTE:ODBC Fundamentals - Handles
Handles 概念
Handles,句柄,是用于标识一个特定事物的透明的32-bit值。
可以简单理解为指针。
在ODBC中,句柄所标识的特定事物包括环境、连接、语句以及描述符。在应用程序中,我们将这些特殊的事物称为实体变量。
当应用调用SQLAllocHandle接口时,驱动器管理器或者驱动会创建一个新的对应类型的实体变量,同时将它的句柄返回给应用。应用程序可以在后续调用ODBC函数时使用这个句柄来标识这个特定实体变量。
驱动器管理器和驱动通过句柄来定位实体变量相关的信息。
下面的例子创建了两个语句句柄,hstmtOrder和hstmtLine,分别用来标识获取sales orders和sales order line numbers的结果集的语句。后续可以使用这两个句柄决定从哪个结果集中获取数据。
SQLHSTMT hstmtOrder, hstmtLine; // Statement handles.
SQLUINTEGER OrderID;
SQLINTEGER OrderIDInd = 0;
SQLRETURN rc;
// Prepare the statement that retrieves line number information.
SQLPrepare(hstmtLine, "SELECT * FROM Lines WHERE OrderID = ?", SQL_NTS);
// Bind OrderID to the parameter in the preceding statement.
SQLBindParameter(hstmtLine, 1, SQL_PARAM_INPUT, SQL_C_ULONG, SQL_INTEGER, 5, 0,
&OrderID, 0, &OrderIDInd);
// Bind the result sets for the Order table and the Lines table. Bind
// OrderID to the OrderID column in the Orders table. When each row is
// fetched, OrderID will contain the current order ID, which will then be
// passed as a parameter to the statement tofetch line number
// information. Code not shown.
// Create a result set of sales orders.
SQLExecDirect(hstmtOrder, "SELECT * FROM Orders", SQL_NTS);
// Fetch and display the sales order data. Code to check if rc equals
// SQL_ERROR or SQL_SUCCESS_WITH_INFO not shown.
while ((rc = SQLFetch(hstmtOrder)) != SQL_NO_DATA) {
// Display the sales order data. Code not shown.
// Create a result set of line numbers for the current sales order.
SQLExecute(hstmtLine);
// Fetch and display the sales order line number data. Code to check
// if rc equals SQL_ERROR or SQL_SUCCESS_WITH_INFO not shown.
while ((rc = SQLFetch(hstmtLine)) != SQL_NO_DATA) {
// Display the sales order line number data. Code not shown.
}
// Close the sales order line number result set.
SQLCloseCursor(hstmtLine);
}
// Close the sales order result set.
SQLCloseCursor(hstmtOrder);句柄只对创建它们的ODBC 组件有意义,即,只有驱动器管理器能够解析驱动器管理器的句柄,只有驱动能够解析它自己的句柄。
例如,在上面例子中,试想驱动分配了一个结构体来存放一个语句的信息,并且返回了指向这个结构体的指针,将其作为语句句柄。
当应用调用SQLPrepare时,它将一个SQL语句和该语句的句柄作为参数传入。驱动将SQL语句发送给数据源,数据源将SQL语句进行prepare,然后返回一个access plan对应的标识符。驱动通过句柄来找到存储这个标识符的结构体。
当应用调用SQLExecute生成结果集时,它将同一个句柄作为参数传入。驱动通过句柄从结构体中获取access plan的标识符,然后将标识符发送给数据源,告诉数据源使用哪个执行计划来执行SQL语句。
Driver Manager' Role in the Connection Process
应用不能直接调用驱动的函数,而是调用相同函数名的驱动器管理器函数,驱动器管理器再调用驱动函数。通常来说,这些调用几乎是瞬时完成的。例如,应用调用驱动器管理器的SQLExecute函数,经过一些错误检查,驱动器管理器调用驱动的SQLExecute函数。
Environment Handles环境句柄
environment,环境,是一个获取数据的全局上下文。与上下文关联的是全局的信息,包括:
- 环境状态
- 当前环境级别诊断信息
- 环境当前分配的连接句柄
- 每一个环境属性的放弃设置
在实现ODBC的一段代码中,一个环境句柄标识一个包含这些环境信息的结构体。
环境句柄在ODBC应用中并不常用。通常用来调用SQLDataSources和SQLDrivers,有时用于调用SQLAllocateHandle,SQLEndTran,SQLFreeHandle,SQLGetDiagField和SQLGetDiagRec。
实现ODBC的每段程序中包含都包含一个或多个环境句柄。例如,驱动器管理器为每一个建立连接的应用维护一个独立的环境句柄。环境句柄通过SQLAllocHandle分配,通过SQLFreeHandle释放。
Connection Handles连接句柄
一个连接包含一个驱动和一个数据源。一个连接句柄标识一个连接。连接句柄不止决定了使用哪个驱动,还决定了使用哪个数据源。在实现ODBC的一段代码中,连接句柄标识一个包含连接信息的结构体。具体包含的连接信息包括:
- 连接状态
- 当前连接级别诊断信息
- 在本连接上当前创建的语句句柄和描述符句柄
- 每一个连接属性的当前设置
ODBC理论上不会阻止多并发连接,如果驱动支持多并发连接,则ODBC支持。因此,可能在一个特定的ODBC环境里存在多个连接句柄,这些连接句柄可以指向多个驱动和数据源或者单个驱动多个数据源,甚至可以多个连接指向单个驱动和单个数据源。一些驱动会对活跃的会话数进行限制;SQLGetInfo的SQL_MAX_DRIVER_CONNECTIONS选项指定一个驱动支持多少个活跃会话数。
连接句柄主要用在与数据源建立连接、释放连接、获取驱动和数据源信息、获取诊断信息、执行事务等场景。也可用在设置OR获取连接属性以及获取一个SQL语句的本地格式等场景。
建立连接:SQLCconnect/SQLDriverConnect/SQLBrowseConnect
释放连接:SQLDisconnect
获取驱动和数据源信息:SQLGetInfo
获取诊断信息:SQLGetDiagField/SQLGetDiagRec
执行事务:SQLEndTran
设置OR获取连接属性:SQLSetConnectAttr/SQLGetConnectAttr
获取SQL语句的本地格式:SQLNativeSql
创建连接句柄:SQLAllocHandle
释放连接句柄:SQLFreeHandle
Statement Handles语句句柄
statement,语句,通常会被简单地理解为一个SQL语句,例如SELECT * FROM Employee。然而一个语句远不止是一个SQL语句,它包含SQL语句相关联地所有信息,例如语句生成的结果集和语句执行过程中用到的参数。一个语句甚至不是必须要有一个应用定义的SQL语句。例如,当在一个语句上执行了一个catalog函数比如SQLTables,它实际执行了一个预先定义的返回一系列表名的SQL语句。
一个语句句柄标识一个语句。一个语句会与一个连接相关联,同时一个连接可以有多个相关联的语句。有些驱动对支持的活跃语句数量有限制;可以通过SQLGetInfo函数的SQL_MAX_CONCURRENT_ACTIVITIES选项设置一个驱动允许在一个连接上支持多少个活跃语句数量。当一个语句有待定结果时,这个语句被定义为活跃的语句。有待定结果包含三种情形:结果是一个结果集;结果是被INSERT/UPDATE/DELETE语句影响的行数;数据被多个调用发送给SQLPutData。
在实现ODBC的一段代码中,语句句柄标识一个包含语句信息的结构体。结构体中的语句信息包含:
- 语句状态
- 当前语句级别的诊断信息
- 应用程序绑定到语句参数和结果集列上的变量地址(参数、列、变量可以有多个)
- 每个语句属性的当前设置
大部分的ODBC函数中都会用到语句句柄。尤其是绑定参数和结果集列的函数、prepare和execute语句的函数、检索元数据的函数、获取结果集的函数、检索诊断信息的函数等。catalog函数以及一些其他函数也会用到语句句柄。
绑定参数和结果集列的函数:SQLBindParameter/SQLBindCol
prepare和execute语句的函数:SQLPrepare/SQLExecute/SQLExecDirect
检索元数据的函数:SQLColAttribute/SQLDescribeCol
获取结果集的函数:SQLFetch
检索诊断信息的函数等:SQLGetDiagFields/SQLGetDiagRec
catalog函数:SQLColumns/SQLTables
分配和释放语句句柄:SQLAllocHandle/SQLFreeHandle
Descriptor Handles
descriptor,描述符,是一个描述SQL语句参数或者结果集列的元数据集合。描述符可以胜任下面四种角色功能:
- Application Parameter Descriptor(APD),应用参数描述符
包含绑定到一个SQL语句的参数上的应用缓冲区的信息,例如地址、长度、C的数据类型等。 - Implementation Parameter Descriptor(IPD),实现参数描述符
包含SQL语句中的参数的相关信息,例如SQL数据类型、长度、是否可为空等。 - Application Row Descriptor(ARD),应用行描述符
包含绑定到一个结果集的列上的应用缓冲区的信息,例如地址、长度、在C语言中的数据类型等。 - Implementation Row Descriptor(IRD),实现行描述符
包含一个结果集中的列的信息,例如SQL数据类型、长度、是否可为空等。
以上四种类型的描述符(每个类型承担自己的功能角色)是在语句分配时自动分配的。它们被称为自动分配型描述符,并且永远是与对应的语句相关联的。
除了自动分配型描述符,应用程序还可以通过SQLAllocHandle函数显式地分配描述符。这类描述符被称为显式分配的描述符。这类描述符在一个连接上分配,可以被关联到一个或多个该连接上的语句,用来作为这些语句的APD或ARD。
ODBC操作大部分可以不需要应用程序显式地使用描述符。不过,对于一些操作,描述符提供了一个方便的捷径。例如,假设一个应用程序想要使用两组不同的缓冲区插入数据。要使用第一组缓冲区,它可能会重复多次调用SQLBindParameter函数,直到将缓冲区绑定到一个INSERT语句的所有参数上,然后执行语句。要使用第二组缓冲区,它可以重复第一组相同的操作,也可以换一种方式:它可以在一个描述符中设置绑定第一组缓冲区,在另一个描述符中设置绑定第二组缓冲区。应用只需简单地调用SQLSetStmtAttr函数将正确的描述符指定为语句上的APD,就可以完成两组绑定缓冲区之间地切换。
State Transitions状态切换
ODBC为每个环境、连接、语句都定义了离散的状态。
例如,环境有三种可能的状态:未分配、分配、连接。
未分配:没有分配环境;
分配:分配了环境,但是没有分配连接;
连接:分配了环境,并且分配了一个或多个连接。
环境有3种可能的状态,连接有7种可能的状态,语句有13种可能的状态。
当应用调用一个或多个特定的函数,并且将句柄传入时,该句柄所标识的对象,从一个状态转变为另一个状态。这种状态的变化称为状态切换。例如,调用SQLAllocHandle分配一个环境句柄,将环境状态由未分配切换为已分配,调用SQLFreeHandle释放该环境句柄,将环境状态由已分配切换回未分配。
ODBC 定义了有限数量的合法状态转换,换句话说,即必须按特定顺序调用函数。
有一些函数,例如SQLGetConnectAttr,完全不会影响状态。其他的函数回影响一个特定对象的状态。例如SQLDissconnect将一个连接从连接状态切换为已分配状态。最后还有一些函数影响多个对象的状态,例如,调用SQLAllocHandle分配连接句柄,将一个连接从未分配切换到已分配状态,同时将环境从已分配状态切换到连接状态。
如果应用程序调用函数的顺序不对,函数回返回一个状态转换错误。例如,如果一个环境当前是连接状态,然后应用程序对这个环境调用SQLFreeHandle,此时SQLFreeHandle会返回SQLSTATE HY010(函数顺序错误),因为这个函数只能在环境处于已分配状态时调用。ODBC通过将这种状态转换定义为非法的转换来阻止应用程序在有活跃连接的时候释放环境。
在ODBC的设计中,一些状态的切换是固定的。例如,不能在没有分配一个环境句柄的时候就分配连接句柄,因为分配连接句柄的函数需要环境句柄。其他状态转换由驱动程序管理器和驱动程序强制执行。 例如,SQL Execute 执行prepared状态的语句。 如果传递给它的语句句柄不处于 Prepared 状态,则 SQL Execute 返回 SQLSTATE HY010(函数序列错误)。
从应用程序的角度来看,状态转换通常很简单:合法的状态转换往往与编写良好的应用程序的流程相得益彰。 但对于驱动程序管理器和驱动程序来说状态转换要更加复杂,因为它们必须跟踪环境、每个连接和每个语句的状态。 大部分工作由驱动管理器完成; 驱动程序必须完成的大部分工作都集中在具有待定结果的语句上。
后续的讲解中可能趋向于不去显式地提到状态切换,而去描述函数地调用顺序。例如,执行语句部分会陈述:一个语句必须先通过SQLPrepare函数进行prepare,然后才能通过SQLExecute函数执行它。
关于状态和状态切换的完整描述,包含哪些转换由驱动器管理器检查,哪些转换必须由驱动检查等信息,请参照ODBC Programmer's Reference的附录B:ODBC 状态转换表。

浙公网安备 33010602011771号