[Mono-dev] Show-stopper bug in libodbc.cs

quandary quandary82 at hailmail.net
Mon Jan 16 23:21:33 UTC 2012


Hi,

I've ripped  System.Data.Odbc out of mcs/class, to hold an extended 
little debug-session last Sunday.

Overall, the good message is, that so far, it seems to be working 
perfectly with Sybase, which is strange, but all I need at the moment.

I was also reattempting to read some tables in NorthWind.mdb (the 
MS-Access example file), to test whether Access over ODBC now works.
It's already much better than last time, but unsurprisingly, I've still 
found a few show-stopping bugs, mainly in 
System.Data/System.Data.Odbc/libodbc.cs


You have this (in 6 overloads):

         [DllImport ("odbc32.dll", CharSet = CharSet.Unicode)]
         internal static extern OdbcReturn SQLGetData (
             IntPtr StatementHandle,
             ushort ColumnNumber,
             SQL_C_TYPE TargetType,System.Int64
             byte[] TargetPtr,
             int BufferLen,
             ref int Len);



But MSDN and the header-file in /usr/include defines SQLGetData like this:
SQLRETURN SQLGetData(
       SQLHSTMT       StatementHandle,
       SQLUSMALLINT   Col_or_Param_Num,
       SQLSMALLINT    TargetType,
       SQLPOINTER     TargetValuePtr,
       SQLLEN         BufferLength,
       SQLLEN *       StrLen_or_IndPtr);


Note that SQLLEN thing there:
typedef long SQLLEN

And while
sizeof(long) = 4 = sizeof(int) on x86-32 and x86-64 Windows, as well as 
x86-32 Linux
for x86-64 on Linux sizeof(long) = 8   ==  != sizeof(int) = 4 on x86-64 
Linux

I got all kinds of wired error messages with a definition like that.
(credits for discovering the cause go to g++ warnings)

I fixed it for my system by replacing int with System.Int64, which works 
fine FOR ME, since I only need to care about my system.
You should probably fix it by using IntPtr, but I don't know about 
64-bit Windows.

         [DllImport ("odbc32.dll", CharSet = CharSet.Unicode)]
         internal static extern OdbcReturn SQLGetData (
             IntPtr StatementHandle,
             ushort ColumnNumber,
             SQL_C_TYPE TargetType,
             byte[] TargetPtr,
             int BufferLen,
             ref int Len);



Then there is a "bug" in Function GetValue in OdbcDataReader.cs
Overall, case OdbcType.VarChar seems to be the troublechild.

I did a
"SELECT CompanyName, City From Customers"; // Access, file NorthWind.mdb


The result is really an infinite do-while loop
(i fixed it by just adding a break statement -  no idea how correct that 
is, but I see no reason why this is in a loop at all)

OdbcDataReader --> GetValue --> case OdbcType.VarChar

do {
                         ret = libodbc.SQLGetData (hstmt, ColIndex, 
col.SqlCType, buffer, bufsize, ref outsize);
                         if (ret == OdbcReturn.Error)
                             break;
                         // Fix for strance ODBC drivers (like psqlODBC)
                         if (ret == OdbcReturn.Success && outsize==-1)
                             ret = OdbcReturn.NoData;
                         if (ret == OdbcReturn.Success || ret == 
OdbcReturn.SuccessWithInfo)
                         {
                             if (outsize >= bufsize || outsize == 
(int)OdbcLengthIndicator.NoTotal) // NoTotal = -4
                                 outsize = bufsize - 1;
                             int charCount = 
defaultDecoder.GetChars(buffer, 0, (int) outsize, charBuffer, 0);

                             sb1.Append(charBuffer, 0, charCount);
                             break;   
/////////////////////////////////////////////////////// FIXME: Infinite 
loop here ?
                         }
                     } while (ret != OdbcReturn.NoData);
                     DataValue = sb1.ToString ();
                     break;


(I added the break with the FIXME comment.)

For risksand side effects, read thepackage leaflet, or ask 
yourdoctororpharmacist.


After this fixes, it's now possible to get the list of CompanyName and 
City from the Customers-table in Access !!!
Keep up the good work !

However, there is an additional bug:
For each character with an accent, for example French éèà, etc. and 
German Umlauts (äöüÄÖÜ), it displays one character too few at the end of 
the word...
I guess this is a bug in the decoder, but it's juuuuust a guess.
Again, isql works correct here, so it really is a mono bug, and not an 
mdbtools-deficiency.

But congratulations so far, it's now possible to access and modify an 
Access database with mono on Linux.
(using the mdbtools ODBC driver).


/etc/odbc.ini:

[Access_NorthWind_Traders]
Description = Microsoft Access Database
Database = /root/DBs/Nwind.mdb
Driver = /usr/lib/libmdbodbc.so
Setup =
FileUsage = 1
CPTimeout =
CPReuse =




Additionally, SQLConnect in libodbc.cs
has this attribute:
[DllImport ("odbc32.dll", CharSet = CharSet.Unicode)]

However, if Unicode is specified, it doesn't work with a Firebird 2.5 DSN.
All others work fine, however, isql command-line works with all of them, 
including Firebird.
It also works fine with Firebird on mono if I specify CharSet.Ansi

There are some further bugs afterwards, however.
(SELECT * FROM employee, in the firebird 2.5 employee example-database)
complete with wrong column names (missing underscore in one column), and 
no data if I access that or other rows...
Something is really wrong here. But since FireBird has a managed 
provider for mono, it's largely irrelevant.



If I were you, I would really look that Odbc works.
If it does, you could replace SqlClient, OracleClient, etc, with just a 
wrapper around Odbc.
That way you needn't fix all those classes whenever a new version 
appears (SQL Server, Oracle, Sybase, etc.).
Plus you don't depend on the manufacturers providing an implementation 
for mono (Oracle ODP.NET doesn't provide it).
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.ximian.com/pipermail/mono-devel-list/attachments/20120117/598adf48/attachment-0001.html>


More information about the Mono-devel-list mailing list