Talking about a little understanding of psqlodbc cursor from a bug

This article stems from a bug about psqlodbc that was recently fixed. The bug was also submitted on the git of psqlodbc recently.
The correction code for this bug can be found here:
https://git.postgresql.org/gitweb/?p=psqlodbc.git;a=commit;h=85f6fade3

Speaking of this bug, we want to mention two API functions provided by ODBC: SQLBulkOperations and SQLSetPos .

Regarding these two functions, their usefulness is:

SQLBulkOperations执行大容量插入和大容量书签操作(包括update、 delete和fetch)。
SQLSetPos函数设置在行集中的游标位置,并允许应用程序刷新行集中的数据或用于更新或删除在结果集中的数据。当利用SQLSetPos删除数据时,操作设置为 SQL_DELETE并且将RowNumber设置为要删除的行号(当设置RowNumber为0时删除结果集中的所有数据)

To put it bluntly, you first use the SQLExecute or SQLExecDirect function to execute the SQL text to obtain a result set, and then operate on the result set. The scenario where the bug occurs is when DELETE occurs, that is, when we delete the data in the result set. When the data in the result set is deleted, the Adddeleted() function will be called to record the row number, status information, oid, ctid, etc. of the deleted data in the result set, and save it in a structure array. The structure array is designed to be stored in increasing order, so you may need to sort the structure array every time you delete it. The problem is in this sorting process. When sorting, it should be traversing the array in turn. Each time the for loop ends count+1. As a result, it is directly count+num_field (this is the number of columns in the result set), so as long as the returned If the number of columns in the result set is greater than 1, the bug should occur.

It seems so clear.
We reproduce the program as follows:

(1) 调用SQLSetStmtAttr函数设置SQL文的SQL_ATTR_ROW_ARRAY_SIZE属性值大于1;
(2) 调用SQLExecute或者SQLExecDirect函数执行SQL文;
(3) 调用SQLFetch或SQLFetchScroll函数获取结果集;
(4) (3)中返回的结果集的列数大于1;
(5) (3)中返回的结果集的行数大于1;
(6) 调用ODBC的API函数SQLSetPos或者SQLBulkOperations删除(2)中返回的结果集中的数据;
(7) (6)中删除的记录行数超过一行

Description: SQL_ATTR_ROW_ARRAY_SIZE

指定每次调用SQLFetch或SQLFetchScroll函数返回的结果集行数。 它也用于指定SQLBulkOperations函数的大容量书签操作中的书签数组中的行数。 默认值为 1。

Let the program return 50 pieces of data, but we just can't reproduce it. Helpless, I accidentally increased the returned result set to 200, and the result is now.

Strange, this bug has nothing to do with the number of rows in the result set.

With this doubt, I debugged the code:
at the beginning of the AddDeleted() function, there is this statement:

    if (!QR_get_cursor(res))
        return TRUE;

When the result set function is small, it is returned directly here. . . That is to say, there is no cursor at this time. However, using SQLExecute() to execute SQL, the text is opened with a cursor by default, and the program obtains data from the cursor. Unwilling to admit defeat, I explicitly specified the cursor at the beginning of the test program:

SQLSetCursorName(hstmt, "C1", SQL_NTS);

Still the same result. Weird. So I opened the mylog and commonlog of the ODBC data source:

Discovery:
This cursor is actually closed! ! !

Incredible.
So I inquired the relevant information and got the following understanding:
3. The cache size of the ODBC data source
Every time the SQLExecute or SQLExecDirect function is executed to execute the SQL text, the bottom layer of the program is to call the cursor to return the result set of the number of rows of the cache size to the ODBC at one time. Cache of data sources. If the number of result set rows returned by the SQL text does not exceed the cache size (that is, the cache of the ODBC data source can cache all the result sets of the SQL text), then the ODBC data source will close the cursor. Otherwise the ODBC data source will hold the cursor.

In other words, this is ODBC's own caching mechanism.

So I modified the reproduction program:

(1) 调用SQLSetStmtAttr函数设置SQL文的SQL_ATTR_ROW_ARRAY_SIZE属性值大于1;
(2) 调用SQLExecute或者SQLExecDirect函数执行SQL文;
(3) (2)中SQL文返回的行数大于ODBC数据源的cache size;
(4) 调用SQLFetch或SQLFetchScroll函数获取结果集;
(5) (4)中返回的结果集的列数大于1;
(6) (4)中返回的结果集的行数大于1;
(7) 调用ODBC的API函数SQLSetPos或者SQLBulkOperations删除(2)中返回的结果集中的数据;
(8) (7)中删除的记录行数超过一行

The bug is reproduced.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324738659&siteId=291194637