本文源于最近修正的一个关于psqlodbc的bug,该bug在近期的psqlodbc的git上也有人提交了修正。

关于该bug的修正代码可以看这里:

https://git.postgresql.org/gitweb/?p=psqlodbc.git;a=commit;h=85f6fade3

说道这个bug,我们要提ODBC提供的两个API函数:SQLBulkOperations 和 SQLSetPos

关于这两个函数,它们的用处是:

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

说白了就是你先用SQLExecute或者SQLExecDirect函数执行SQL文获取一个结果集,然后对这个结果集进行操作。bug发生的场景是DELETE的时候,即我们对结果集中的数据进行删除时发生。在对结果集的数据进行删除时,会调用Adddeleted()函数将结果集中被删除的数据的行号、状态信息和oid,ctid等做记录,保存在一个结构体数组中。而结构体数组在设计是要求按照递增的顺序进行存储的,所以你进行每一次删除可能就要对这个结构体数组做排序操作。问题就在这个排序过程中,排序的时候本应该是依次遍历数组,每次for循环结束count+1,结果它这里直接是count+num_field(这个是结果集的列数),所以,只要返回的结果集的列数大于1,bug就应该发生。

似乎就是这样清楚明白。

我们按照以下的构造再现程序:

(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)中删除的记录行数超过一行

说明:SQL_ATTR_ROW_ARRAY_SIZE

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

让程序返回了50条数据,但是我们就是再现不出来。无奈,无意中我把返回的结果集增大到了200,结果居然在现了。

奇怪,这个bug和结果集的行数无关啊。

带着这个疑问,我调试了代码:

在AddDeleted()函数的开头,有这样的语句:

	if (!QR_get_cursor(res))
return TRUE;

当结果集函数较小时,直接在这里就返回了。。。也就是说这个时候没有游标了。可是使用SQLExecute()执行SQL、文是默认有游标打开的,程序从游标中获取数据的。不服输的我又在测试程序开头显示地指定了游标:

SQLSetCursorName(hstmt, "C1", SQL_NTS);

还是一模一样的结果。很奇怪。于是我打开了ODBC数据源的mylog和commonlog:

发现:

这个游标居然被关闭了!!!

不可思议。

于是我查询了相关资料,得到以下的认识:

3.ODBC数据源的cache size

每次执行SQLExecute或者SQLExecDirect函数执行SQL文时,程序底层是调用游标一次性返回cache size大小行数的结果集到ODBC数据源的缓存。如果SQL文返回的结果集行数不超过cache size(即ODBC数据源的缓存能缓存下SQL文的所有结果集),那么ODBC数据源会关闭该游标。否则ODBC数据源会保持该游标。

也就是说,这个是ODBC自己的缓存机制。

于是我修改再现程序:

(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)中删除的记录行数超过一行

再现了该bug。

从一个bug谈谈psqlodbc游标的一点认识的更多相关文章

  1. 由一个bug引发的SQLite缓存一致性探索

    问题 我们在生产环境中使用SQLite时中发现建表报“table xxx already exists”错误,但DB文件中并没有该表.后面才发现这个是SQLite在实现过程中的一个bug,而这个bug ...

  2. 是uibutton跟tableviewcell同步使用一个bug

    这个问题是uibutton跟tableviewcell同步使用一个bug,不关delay一点毛事,证据就是点击事件没问题,so,搜到一个方法解决了这个问题.uibutton分类symbian2+ios ...

  3. 有人向我反馈了一个bug

    我是一个前端开发者,但我想这个故事对任何开发者都会引起共鸣的有人向你反馈了一个 bug. “26 楼会议室的灯亮着.它需要被熄灭.”bug 的备注里写道“你应该能在 5 分钟内搞定,只要按一下开关就好 ...

  4. 从修复 testerhome(rubychina)网站的一个 bug 学习 ruby&rails on ruby

    前言 testerhome: http://testerhome.com/topics/1480 对于一个差点脱离前沿技术人,想要学习ruby,就意味着要放弃熟悉的操作系统windows,熟悉的ide ...

  5. 写了一个bug,最后却变成了feature,要不要修呢?

    事情是这样子的,前不久接到一个需求,为一个游戏开发礼包码功能 通常一款游戏运营期间会搞各种各样的活动吸引玩家,其中最常见的就是发放礼包,  玩家可以通过礼包码兑换礼包. 用礼包码兑换礼包有个一限制,游 ...

  6. NDK中使用pthread多线程中自己写的一个BUG

    在使用pthread进行NDK中的多线程开发时,自己写了一个BUG, void *darkGrayThread(void *args) { ThreadParam *param = (ThreadPa ...

  7. 一个Bug 差点让服务器的文件系统崩溃

    昨天,公司的美国客户发邮件给我,说我的软件出问题了,我查来查去,发现居然是服务器上一个目录无法删除,一删除就报 cannot read from the source file or disk. 如果 ...

  8. puppet的一个Bug

    前篇文章写了使用puppet管理500多台服务器,当然只是一部分,最主要的还是puppet脚本的编写,这个我会在以后的文章中一点一点写出来. 今天要写的是puppet的一个bug,版本是puppet ...

  9. 发现护考上机考试的一个bug:附软件截图(模拟软件)

    目录: 一.文章主旨 二.问题发现的起因 三.bug(问题)描述 四.软件截图 五.我的思考 六.一点期盼 一.文章主旨: 2019年5月18.19.20日,又是一年一度的护资考试(上机考),考试前夕 ...

随机推荐

  1. OGNL概述

    -------------------siwuxie095 OGNL 概述 1.OGNL 即 Object-Graph Navigation Language,对象图导航语言, 它是一个应用于 Jav ...

  2. 旋转图像 · Rotate Image

    [抄题]: You are given an n x n 2D matrix representing an image. Rotate the image by 90 degrees (clockw ...

  3. opennebula 补丁制作与补丁升级

    参考文章:http://www.cnblogs.com/itech/archive/2009/08/19/1549729.htmlhttp://www.ibm.com/developerworks/c ...

  4. Webservice初级问题: FAILED TO READ WSDL document

    这个问题是说明,这个版本的没法下载 犯错的图样 处理方法一: 将网页上xml文档下载,保存在本地,然后错误提示的这几行删除,保存文档,然后从本地调用 (1)右键另存为 保存为文件名a.xml (2)打 ...

  5. jquery.fn.extend() 与 $.jquery 作用及区别

    原文:http://www.cnblogs.com/liu-l/p/3928373.html jQuery.extend()这个方法,主要是用来拓展个全局函数啦,例如$.ajax()这种,要不就是拓展 ...

  6. [label][webSites]一个结合Google地图的图片浏览网站

    内容中包含 base64string 图片造成字符过多,拒绝显示

  7. 转:Entity FrameWork利用Database.SqlQuery<T>执行存储过程并返回参数

    public IEnumerable<Statistic> GetStatistics(IEnumerable<Guid> itemIds) { var ctx = new D ...

  8. 执行计划--WHERE条件的先后顺序对执行计划的影响

    在编写SQL时,会建议将选择性高(过滤数据多)的条件放到WHERE条件的前面,这是为了让查询优化器优先考虑这些条件,减少生成最优(或相对最优)的执行计划的时间,但最终的执行计划生成过滤顺序还是决定这些 ...

  9. Spring学习(五)——集成MyBatis

    本篇我们将在上一篇http://www.cnblogs.com/wenjingu/p/3829209.html的Demo程序的基础上将 MyBatis 代码无缝地整合到 Spring 中. 数据库仍然 ...

  10. centos7 安装SSH

    1.安装OpenSSH服务(CentOS系统默认安装了openssh)      yum install openssh-server -y 2.配置OpenSSH服务(默认的配置已可以正常工作) O ...