使用ODBC 3.0开发,在Blob数据绑定时,Statement类封装了一个方法:
void setBlob(int iParam, Blob *b){
SDWORD len = SQL_LEN_DATA_AT_EXEC(b->getLength());
retcode = retcode = SQLBindParameter(this->hstmt, iParam, SQL_PARAM_INPUT,
SQL_C_BINARY, SQL_LONGVARBINARY, b->getLength(), 0,
(void*)b, b->getLength(), &len);
}
同时,又封装了一个executeUpdate方法:
bool executeUpdate(){
SQLPOINTER data;char buf[4 * 1024] = {0};
retcode = ::SQLExecute(hstmt);
if(retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO)
retcode = ::SQLParamData(hstmt, &data);
while(retcode == SQL_NEED_DATA){
Blob *b = (Blob*)data;
while(true){
SQLLEN blen = b->getBuffer(buf, 4 * 1024);
if(blen == 0) break;
retcode = ::SQLPutData(hstmt, (SQLPOINTER)buf, blen);
if(retcode != SQL_SUCCESS)
throw CSQLException(retcode, getErrorMessage());
}
retcode = ::SQLParamData(hstmt, &data);
}
if(retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO)
throw CSQLException(retcode, getErrorMessage());
return true;
}
结果,执行executeUpdate时出现“[Microsoft][ODBC SQL Server Driver]字符串数据,长度不匹配”的错误。
找了半天,没有发现什么逻辑错误,后来一次偶然的机会在网络上看到类似的代码,它的写法是这样的:
SDWORD len;
retcode = retcode = SQLBindParameter(this->hstmt, iParam, SQL_PARAM_INPUT,SQL_C_BINARY, SQL_LONGVARBINARY, b->getLength(), 0,
(void*)b, b->getLength(), &len);
len = SQL_LEN_DATA_AT_EXEC(b->getLength());
其中,len是之后才赋值的,并且这段代码与后续execute代码在同一个方法体内。
由此可见,其实传进去的这个len指针是在SQLPutData时才真正起作用,因此要保证在SQLPutData时它的值有效且正确。
至此才真正发现原因,原来还是犯了低级错误:由于len是setBlob方法的内部变量,因此当SQLBindParameter返回之后,它的地址就无效了,而后面调用executeUpdate时就会出错(至少是长度不对,也可能比较严重,内存不能访问)。
之后将SDWORD len;改为SDWORD *len = new SDWORD();,然后再赋值就可以了。但有一点小Bug,就是会有内存泄漏(可以考虑使用类来封装,比如放在Blob类中来下定义,这样就不要额外分配内存单元,不便于回收)。