从一个bug谈谈psqlodbc游标的一点认识
本文源于最近修正的一个关于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游标的一点认识的更多相关文章
- 由一个bug引发的SQLite缓存一致性探索
问题 我们在生产环境中使用SQLite时中发现建表报“table xxx already exists”错误,但DB文件中并没有该表.后面才发现这个是SQLite在实现过程中的一个bug,而这个bug ...
- 是uibutton跟tableviewcell同步使用一个bug
这个问题是uibutton跟tableviewcell同步使用一个bug,不关delay一点毛事,证据就是点击事件没问题,so,搜到一个方法解决了这个问题.uibutton分类symbian2+ios ...
- 有人向我反馈了一个bug
我是一个前端开发者,但我想这个故事对任何开发者都会引起共鸣的有人向你反馈了一个 bug. “26 楼会议室的灯亮着.它需要被熄灭.”bug 的备注里写道“你应该能在 5 分钟内搞定,只要按一下开关就好 ...
- 从修复 testerhome(rubychina)网站的一个 bug 学习 ruby&rails on ruby
前言 testerhome: http://testerhome.com/topics/1480 对于一个差点脱离前沿技术人,想要学习ruby,就意味着要放弃熟悉的操作系统windows,熟悉的ide ...
- 写了一个bug,最后却变成了feature,要不要修呢?
事情是这样子的,前不久接到一个需求,为一个游戏开发礼包码功能 通常一款游戏运营期间会搞各种各样的活动吸引玩家,其中最常见的就是发放礼包, 玩家可以通过礼包码兑换礼包. 用礼包码兑换礼包有个一限制,游 ...
- NDK中使用pthread多线程中自己写的一个BUG
在使用pthread进行NDK中的多线程开发时,自己写了一个BUG, void *darkGrayThread(void *args) { ThreadParam *param = (ThreadPa ...
- 一个Bug 差点让服务器的文件系统崩溃
昨天,公司的美国客户发邮件给我,说我的软件出问题了,我查来查去,发现居然是服务器上一个目录无法删除,一删除就报 cannot read from the source file or disk. 如果 ...
- puppet的一个Bug
前篇文章写了使用puppet管理500多台服务器,当然只是一部分,最主要的还是puppet脚本的编写,这个我会在以后的文章中一点一点写出来. 今天要写的是puppet的一个bug,版本是puppet ...
- 发现护考上机考试的一个bug:附软件截图(模拟软件)
目录: 一.文章主旨 二.问题发现的起因 三.bug(问题)描述 四.软件截图 五.我的思考 六.一点期盼 一.文章主旨: 2019年5月18.19.20日,又是一年一度的护资考试(上机考),考试前夕 ...
随机推荐
- torque
torque - 必应词典 美[tɔrk]英[tɔː(r)k] n.(使机器等旋转的)转矩 网络扭矩:扭力:力矩 变形过去分词:torqued:现在分词:torquing:第三人称单数:torques ...
- 单词搜索 II · Word Search II
[抄题]: 给出一个由小写字母组成的矩阵和一个字典.找出所有同时在字典和矩阵中出现的单词.一个单词可以从矩阵中的任意位置开始,可以向左/右/上/下四个相邻方向移动. 给出矩阵: doafagaidca ...
- [DT] 数据结构术语中英文对照
数据结构术语中英文对照 数据 Data 数据元素 Data element 数据项 Data item 数据结构 Data structure 逻辑结构 Logical structure 数据类型 ...
- 关于简单的三层的简化(bll,dal,model)的封装这里全部都在一个文件主要在于明白意思
using System;using System.Collections.Generic;using System.Linq;using System.Text; namespace 封装泛型CRU ...
- firstpage 2015/5/21
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="firstPage.aspx ...
- 是什么优化让 .NET Core 性能飙升?(转)
欢迎大家持续关注葡萄城控件技术团队博客,更多更好的原创文章尽在这里~~ .NET Core(开放源代码,跨平台,x-copy可部署等)有许多令人兴奋的方面,其中最值得称赞的就是其性能了. 感谢所有社区 ...
- C语言 void 万能类型
C中 void类型其实是一种万能类型, 也就是说 我们的 void 可以代表任意一种类型,也就是说他的范围要比具体的一种类型要宽广, 例如: int *p = NULL;void *ptr = p; ...
- Alpha冲刺 - (6/10)
Part.1 开篇 队名:彳艮彳亍团队 组长博客:戳我进入 作业博客:班级博客本次作业的链接 Part.2 成员汇报 组员1(组长)柯奇豪 过去两天完成了哪些任务 基于ssm框架的前后端交互测试,结合 ...
- Oracle E-Business Suite并发请求的优先级(Concurrent Request Priority)
不少用户抱怨自己的Oracle E-Business Suite并发请求(Concurrent Request)提交了好久,但还是一直在排队,等了好久还没有执行.用户希望对于一些重要性程度高.响应要求 ...
- linux服务器下配置多tomcat
车辆交易用的系统模块,正在做.老板要看看,以便车城那边的人提出意见.于是在服务器上再次增加一个tomcat. 以前是配置过的,配置过程其实很简单,这次太大意了,找了半天问题. 首先是拷贝一个tomca ...