9 Replies Latest reply on Apr 18, 2016 7:12 AM by TSGal

    SQLGetData ODBC call sometimes misses setting the terminating null

    ranja

      Summary

      SQLGetData ODBC call sometimes misses setting the terminating null

      Product

      FileMaker Server

      Version

      13

      Operating system version

      Windows 7 32bit, Windows Server 2008R2 SP1 64bit

      Description of the issue

      When SQLGetData function returns data, it must set the terminating null character in the buffer.
      However, in some cases SQLGetData forgets doing this and fills the whole buffer with non-zero values.

      Steps to reproduce the problem

      I tested with the following C++ code to get data from a FileMaker database via FileMaker ODBC driver.(a Win32 console application with MFC enabled)

                     {
                CDatabase db;
                if (db.OpenEx(_T("DSN=TestODBC;UID=admin;PWD=;"), CDatabase::noOdbcDialog))
                {
                     _tprintf(_T("Opened\n"));

                     //実行
                     CRecordset rs(&db);
                     try{
                          rs.Open(CRecordset::forwardOnly, _T("SELECT Field2 FROM TestODBC WHERE Field1 = 7"));

                          //値を表示
                          if (!rs.IsEOF())
                          {
                               SQLLEN nActualSize;
                               DWORD nLen = 2047;

                               wchar_t pvData[2048];//1 char larger

                               RETCODE retCode = ::SQLGetData(rs.m_hstmt, 1, SQL_C_WCHAR, pvData, (nLen)*sizeof(wchar_t), &nActualSize);

                               for (int i = 0; i < nLen+1; i++){
                                    _tprintf(_T("%ld: %5c\n"), i, pvData[i]);
                               }
                               _tprintf(_T("nActualSize=%ld\n"), nActualSize);

                               retCode = ::SQLGetData(rs.m_hstmt, 1, SQL_C_WCHAR, pvData, (nLen)*sizeof(wchar_t), &nActualSize);

                               for (int i = 0; i < nLen+1; i++){
                                    _tprintf(_T("%ld: %5c\n"), i, pvData[i]);
                               }
                               _tprintf(_T("nActualSize=%ld\n"), nActualSize);

                          }
                     }
                     catch (...){
                          _tprintf(_T("Exception thrown\n"));
                     }

                     rs.Close();
                     db.Close();
                }
           }


      I have set the buffer size to 2047*sizeof(wchar_t) bytes because Microsoft .NET Framework's OdbcDataReader class uses this value.

      The FileMaker field 'Field2' contains a text "01234567890123456789...67Last1", whose length is 4093 (Length(Field2) = 4093).

      After the second call of SQLGetData, the buffer pvData is filled with

      ...
      pvData [2040] = '6'
      pvData [2041] = '7'
      pvData [2042] = 'L'
      pvData [2043] = 'a'
      pvData [2044] = 's'
      pvData [2045] = 't'
      pvData [2046] = '1'


      However, pvData [2046] ought to be 0 (the terminating null).
      (SQLGetData seems to set pvData[2047] to 0, but this is beyond the buffer boundary.)

      When I used DataDirect 32-BIT SequeLink 5.5 ODBC driver along with a FileMaker 9 database, the contents of pvData were:
      ...
      pvData [2040] = '6'
      pvData [2041] = '7'
      pvData [2042] = 'L'
      pvData [2043] = 'a'
      pvData [2044] = 's'
      pvData [2045] = 't'
      pvData [2046] = '\0'

      The final character '1' is not included here.  This is natural.  You just need to call SQLGetData one more time.  As for FileMaker ODBC driver, one more SQLGetData call returns with a sign of NO DATA.

      Because of this irregular behavior, Microsoft .NET Framework's OdbcDataReader GetString method goes into an infinite loop and the program gets stuck.
      Both 32bit and 64bit driver have this issue.

      Above, I used so-called '1-byte' characters as a text data.  If I use 2-byte (Japanese) characters, only the first 1364 elements of pvData are filled with data.

      e.g.
      ...
      pvData [1362] = '2'
      pvData [1363] = '3'
      pvData [1364] = '\0'
      ...

      Expected result

      SQLGetData function is supposed to set the terminating null within the given buffer area.

      Actual result

      In some cases, the whole buffer is filled with non-null values.
      When Japanese characters are used, only 1/3 of the data is returned at each call.

      Workaround

      None