在“实战OLEDB编程”系列文章中,后面的几篇文章中讨论了一下关于延迟提交的问题,有网友回复说新插入的行无法延迟提交,并说是微软的一个bug,本着怀疑一切和刨根问底的精神,我对这个问题进行了深入的实验和研究,结果发现,这种说法很是不负责任的。现在可以肯定的说新插入行的延迟提交是完全没有任何问题的。
至于为什么有网友说不能延迟提交呢?我也分析了一下原因,结果发现,有个关键原因导致新插入行的延迟提交不可行:
其实最大的罪魁祸首就是绑定的错误,主要就是错误的绑定了第0列,我在“七”文章中发布的完整例子代码中就错误的绑定了第0列,结果我自己也被插入问题搞得焦头烂额,最后我仔细的阅读了OLEDB的帮助后发现,原来InsertRow在失败后,会在你插入数据的列Status中写入具体某一列值插入失败的原因。当时我看到第0列这个状态总是DBSTATUS_E_PERMISSIONDENIED,直译过来就可以理解为是不允许插入,为什么不允许插入呢?疯掉,然后我仔细的看了代码后发现,原来创建绑定结构的循环有问题,原来是:
rgBindings = (DBBINDING*)GRS_ALLOC(cColumns * sizeof(DBBINDING));
for( iCol = 0; iCol < cColumns; iCol++ )
{
rgBindings[iCol].iOrdinal = rgColumnInfo[iCol].iOrdinal;
rgBindings[iCol].dwPart = DBPART_VALUE
|DBPART_LENGTH|DBPART_STATUS;
rgBindings[iCol].obStatus = dwOffset;
rgBindings[iCol].obLength = dwOffset + sizeof(DBSTATUS);
rgBindings[iCol].obValue = dwOffset+sizeof(DBSTATUS)+sizeof(ULONG);
rgBindings[iCol].dwMemOwner = DBMEMOWNER_CLIENTOWNED;
rgBindings[iCol].eParamIO = DBPARAMIO_NOTPARAM;
rgBindings[iCol].bPrecision = rgColumnInfo[iCol].bPrecision;
rgBindings[iCol].bScale = rgColumnInfo[iCol].bScale;
rgBindings[iCol].wType = rgColumnInfo[iCol].wType;
rgBindings[iCol].cbMaxLen = rgColumnInfo[iCol].ulColumnSize;
dwOffset = rgBindings[iCol].cbMaxLen + rgBindings[iCol].obValue;
dwOffset = GRS_ROUNDUP(dwOffset);
}
大家可以注意下iCol的起始值,原来是0?!呵呵呵,看来我也是老糊涂了,怎么就绑到了第0列呢?哈哈哈,口口声声说第0列有特殊用途,结果自己还去绑定它。赶快改成如下模样:
rgBindings = (DBBINDING*)GRS_ALLOC(cColumns * sizeof(DBBINDING));
for( iCol = 1; iCol < cColumns; iCol++ )
{
rgBindings[iCol - 1].iOrdinal = rgColumnInfo[iCol].iOrdinal;
rgBindings[iCol - 1].dwPart = DBPART_VALUE|DBPART_LENGTH|DBPART_STATUS;
rgBindings[iCol - 1].obStatus = dwOffset;
rgBindings[iCol - 1].obLength = dwOffset + sizeof(DBSTATUS);
rgBindings[iCol - 1].obValue = dwOffset+sizeof(DBSTATUS)+sizeof(ULONG);
rgBindings[iCol - 1].dwMemOwner = DBMEMOWNER_CLIENTOWNED;
rgBindings[iCol - 1].eParamIO = DBPARAMIO_NOTPARAM;
rgBindings[iCol - 1].bPrecision = rgColumnInfo[iCol].bPrecision;
rgBindings[iCol - 1].bScale = rgColumnInfo[iCol].bScale;
rgBindings[iCol - 1].wType = rgColumnInfo[iCol].wType;
rgBindings[iCol - 1].cbMaxLen = rgColumnInfo[iCol].ulColumnSize;
dwOffset = rgBindings[iCol - 1].cbMaxLen + rgBindings[iCol - 1].obValue;
dwOffset = GRS_ROUNDUP(dwOffset);
}
这样一来都是从第一列开始绑定,但是这里有个问题,就是如果没有返回第0列那么这个循环就会少绑定一列,当然这难不倒我们,只需要判断rgColumnInfo[0].iOrdinal == 0就可以明确的知道有没有返回第0列,这样循环的上届无非就是最大列数或最大列数减1即可。这个就看实际编码的需要了,当然如何判断和组织这个绑定结构,大家应该不会再有疑问了。
将绑定修改后,我对插入部分做了如下测试:
pCurData = (BYTE*)pData + (dwOffset * iRow);
//取得一行的数据
GRS_COM_CHECK(pIRowset->GetData(rghRows[iRow],phAccessor,pCurData));
//pCurData中已经包含了结果数据,显示或者进行处理
//......
//复制插入一些行:
pNewData = (BYTE*)GRS_ALLOC(dwOffset);
CopyMemory(pNewData,pCurData,dwOffset);
GRS_COM_CHECK(pIRowsetChange->InsertRow(NULL
,phAccessor,pNewData,&hNewRows));
//使用相同的行缓冲再插入两行
GRS_COM_CHECK(pIRowsetChange->InsertRow(NULL
,phAccessor,pNewData,&hNewRows));
GRS_COM_CHECK(pIRowsetChange->InsertRow(NULL
,phAccessor,pNewData,&hNewRows));
在最后使用一个Update:
GRS_COM_CHECK(pIRowsetUpdate->Update(NULL,0
,NULL,&dwUpdateRows,&phUpdateRows,&pUpdateStatus));
GRS_COM_CHECK(pIRowset->ReleaseRows(dwUpdateRows
,phUpdateRows,NULL,NULL,NULL));
最后在数据表中就多出来3行相同的数据,至此插入行的延迟提交问题算是圆满的找到答案了,大家可以放心的使用新插入行的延迟提交了。
这里需要总结的就是如何找到插入失败时的问题,就是靠那个列状态,像下面这样查看插入失败后列的状态,然后对应找到DBSTATUS_开头的状态值的宏定义,就可以具体知道插入失败是为什么了:
DBSTATUS*pStatus = (DBSTATUS*)((BYTE*)pNewData + rgBindings[iCol].obStatus);
当然如果*pStatus是0,就说明该列正常,继续查看下一列即可,直到找到状态值不是0的列,找到宏定义,然后查看OLEDB帮助中的说明,就知道是为什么不能插入了。
(未完待续)