本文源于最近修正的一个关于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. Django 1.10.2 模型数据库操作

    首先我的django 版本 >>> django.VERSION (1, 10, 2, u'final', 0) setting.py: DATABASES = { 'default ...

  2. 输入输出参数 inout

    输入输出参数 inout 函数参数默认是常量.试图在函数体中更改参数值将会导致编译错误(compile-time error).这意味着你不能错误地更改参数值.如果你想要一个函数可以修改参数的值,并且 ...

  3. innodb count优化测试

    对于索引优化真的是门课题,先来研究下最平常的问题,innodb引擎下 怎么让count(*)快一点. 首先需要清楚 innodb 默认是对主键建立聚簇索引,如果没有主键,那就是对具有唯一且非空值的索引 ...

  4. Visual Studio工具 vcpkg简介

    博客参考: https://blog.csdn.net/cjmqas/article/details/79282847#43-%E7%A7%BB%E9%99%A4%E5%85%A8%E5%B1%80% ...

  5. 邮槽 匿名管道 命名管道 剪贴板 进程通讯 转自http://www.cnblogs.com/kzloser/archive/2012/11/04/2753367.html#

    邮槽 通信流程: 服务器 客户端 注意: 邮槽是基于广播通信体系设计出来的,它采用无连接的不可靠的数据传输 邮槽可以实现一对多的单向通信,我们可以利用这个特点编写一个网络会议通知系统,而且实现这一的系 ...

  6. 遍历ListView,查出每一项的内容

    private ListView.OnItemClickListener showItemDetail = new ListView.OnItemClickListener() { public vo ...

  7. DB2日期转格式化字符串

    DB2  应该有个  TO_CHAR  的函数. 用来把 日期 转换为 字符串 1 2 3 4 5 6 7 8 9 10 db2 => SELECT db2 (cont.) =>   TO ...

  8. docker daemon文件/etc/docker/daemon.json配置

    On Linux The default location of the configuration file on Linux is /etc/docker/daemon.json. The --c ...

  9. Web挖掘

    Web挖掘 Web挖掘的目标是从Web的超链接.网页内容和使用日志中探寻有用的信息.依据Web挖掘任务,可以划分为三种主要类型:Web结构挖掘.Web内容挖掘和Web使用挖掘.Web结构挖掘简单的说就 ...

  10. 安装配置BITS上传服务

    IIS 6.0和IIS 7.0 支持安装BITS上传组件. 下面以IIS7.0为例安装配置bits上传服务. 1.安装 首先确定服务器已经按装IIS服务.依次打开服务管理器->功能->添加 ...