Skip to Content.
Sympa Menu

freetds - [freetds] Possible bug in ODBC driver

freetds AT lists.ibiblio.org

Subject: FreeTDS Development Group

List archive

Chronological Thread  
  • From: "Ou Liu" <ou AT qbt.com>
  • To: <freetds AT lists.ibiblio.org>
  • Subject: [freetds] Possible bug in ODBC driver
  • Date: Wed, 15 Jan 2003 16:30:52 -0500

Hi, everyone
When I try to use FreeTDS's ODBC driver on Solaris to fetch some big
tables. It will return error. The problem is like:
(1) I am using SPARC/Solaris 8 as client machine, iODBC as driver
manager,
FreeTDS as odbc driver, MSSQL2K on another Win2K machine as server.
(2) Creat table 'big' of only one column data1 which is
nvarchar(200), and
insert into it about 1000 records.
My code is like:
----------------------------------------------------------------------------
--------------------------------------
/* RandomChar
* Generate series of random rubbish every time it called, could be better
but it's enough for now
*/
#define MAX_QUERY_LEN 1024
char query[MAX_QUERY_LEN];
char* RandomStr(int i)
{
static str[100];
srand(i);
itoa(rand(), (char*) str, 16);
return (char*) str;
}
int main(int argc, char *argv[])
{
Init(); // Connect with the Server
sprintf(query, "use porter_test;");
ExecuteQuery(); // Execute the query to select a database
sprintf(query, "drop table big;");
ExecuteQuery(); // Execute the query to drop table 'big' if it exists
sprintf(query, "create table big (data1 nvarchar(200));");
ExecuteQuery(); // Execute the query to create a table 'big'
for (int i = 0; i < 1000; i++)
{
sprintf(query, "insert into big values(\'%s\')",
RandomStr(i));
ExecuteQuery(); // Execute the query to insert some rubbish
}
Exit(); // Release all handles, and disconnect.
}
----------------------------------------------------------------------------
-------------------------------------- I can't post the code of Init ,Exit
and ExecuteQuery at here since they are too long( they called our ADO
functions). But it's very easy to reproduce the Init ,Executequery, Exit,
since their function is obvious.

(3) Now when I use the following code to read the database, it will
always
fail on SQLGetData
----------------------------------------------------------------------------
--------------------------------------
...
/*
* Fetch data
*/
SQLRETURN iRet = SQLFetch(m_hstmt);

/* If we have no data, then we met the EOF */
if (iRet == SQL_NO_DATA_FOUND)
{
ODBC_Close();
m_bEOF = TRUE;
FreeFields();
return SetErrorsBasedOnMyErrorCode(\
ADOUNIXHelper_ERROR_WRONG_ROW_OFFEST,
"No data any more");
}

for (SQLSMALLINT col = 0; col < m_sFieldNum; col++)
{
...
SQLDescribeCol(m_hstmt, col + 1, (SQLCHAR *) name, \
sizeof(name), NULL, &colType, &colSize, \
&colScale, &colNullable);
switch(colType)
{
...
case SQL_LONGVARCHAR:
case SQL_VARCHAR:
case SQL_CHAR:
default:
{
colType = SQL_C_CHAR;
m_CurRecord[col].vt.vt = VT_BSTR;
m_CurRecord[col].vt.bstrVal = NULL;

/* should be colSize + 2, otherwize the last character will
* disappear if the size of column is just of size 'colSize'
* I don't know why.
*/
size_t real_size = REASONABLE_SIZE(colSize + 2);
m_CurRecord[col].cCharData = (char*) malloc(\
real_size);

/* Check if we got the memory we need */
if (m_CurRecord[col].cCharData == NULL)
{
FreeFields();
ODBC_Close();
return SetErrorsBasedOnMyErrorCode(\
ADOUNIXHelper_ERROR_NO_MEMORY, \
"Sorry, No memory.");
}
iRet = SQLGetData(m_hstmt, col + 1, colType, \
m_CurRecord[col].cCharData, real_size, &cbSize);

/* If there are more data */
if (iRet == SQL_SUCCESS || iRet == SQL_SUCCESS_WITH_INFO)
{
while (cbSize == SQL_NO_TOTAL)
{
/* memory original allocated is not
enough,now we double it */
m_CurRecord[col].cCharData = (char*) realloc(\
m_CurRecord[col].cCharData, real_size
* 2);

/* new_start is used to recieve the left data
from driver */
char *new_start = m_CurRecord[col].cCharData
+ real_size;

/* now we really allocate for cCharData
memory of size
* real_size
*/
real_size *= 2;

/* Try luck this time */
iRet = SQLGetData(m_hstmt, col + 1, colType, \
new_start,real_size, &cbSize);

/* problems? if any jump out for treatment */
if (iRet != SQL_SUCCESS && iRet !=
SQL_SUCCESS_WITH_INFO)
break;
}
}
}
break;
...
----------------------------------------------------------------------------
--------------------------------------
Here REASONABLE_SIZE(colSize + 2); is the minium between the
colSize+2 and
32k, since sometimes it will reutrn a size of 4G if the type is nText in
MSSQL. I know the code is confusing, but what really important is:
(1) The code can't fetch all rows, it will always fail some time at
the
SQLGetData, however the SQLFetch ust before it will succeed.
(2) the row # it will fail is randomly. that is it may can fetch 300
rows
this time and 250 rows next time before it fail on the SQLGetData.
(3) The problem is in the function convert_tds2sql since it return a
value
less than 0, but I didn't peek into the convert_tds2sql yet.
(4) The /* If there are more data */ part is never executed in such a
tiny
database. So it should be concerned when analysizing the reason of problem.
(4) These codes runs quite well on win32 and will return all rows
using the
MS ODBC driver.

Any ideas?
Thanks
Ou





  • [freetds] Possible bug in ODBC driver, Ou Liu, 01/15/2003

Archive powered by MHonArc 2.6.24.

Top of Page