Bug #1316 Sending long data by SQLBindSQLPutData and SQLParamData
Submitted: 16 Sep 2003 23:55 Modified: 16 Oct 2003 22:33
Reporter: Lev Kadisov Email Updates:
Status: Closed Impact on me:
None 
Category:Connector / ODBC Severity:S2 (Serious)
Version:3.51.06.00 & 2.50.39.00 OS:Windows (Win98)
Assigned to: Venu Anuganti CPU Architecture:Any

[16 Sep 2003 23:55] Lev Kadisov
Description:
Calling SQLPutData() after several call SQLBindParameter() and one call SQLBindParameter() with SQL_LEN_DATA_AT_EXEC is damages links first parameters.

I tested this error with MySQL 3.23 + MyODBC 2.50.39.00 
and MySQL 4.0.13 + MyODBC 3.51.06.00.

How to repeat:
I created table 'objdata' by command by ODBC-function:
CREATE TABLE objdata ( id INT NOT NULL, u_time_sec  INT NOT NULL, u_time_msec INT NOT NULL, pdata_len   INT NOT NULL, pdata       BLOB NOT NULL );

or

CREATE TABLE objdata ( id INT, u_time_sec  INT, u_time_msec INT, pdata_len  INT, pdata BLOB );

Then I want save my long data by several parts with ODBC:

    const int POS_BLOB_DATA = 5;
    const int MAX_PART_SIZE = 16;

    SQLHSTMT     stmt;
    char *pdata;
    int datalen;
    int dynData;
    int commonLen;

    SQLLEN  resUTimeSec;
    SQLLEN  resUTimeMSec;
    SQLLEN  resDataLen;
    SQLLEN  resData;
    .......

    dynData = 1; // If dynData = 1 then we will not use SQLPutData() 
                 // and SQLParamData()
                 // if this dynData = 0 then all OK, data are correctly stored !
    SQLPrepare(stmt, "INSERT INTO objdata VALUES ( ?, ?, ?, ?, ? )", SQL_NTS);

    .........

    resId = 0;
    resUTimeSec = 0;
    resUTimeMSec = 0;
    resDataLen = 0;
    resData = dynData ? SQL_LEN_DATA_AT_EXEC(0) : datalen;

    sqlRes = SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_SLONG,
          SQL_INTEGER, 0, 0, &id, 0, &resId);
    // check sqlRes - always OK
    .........

    sqlRes = SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_SLONG,
          SQL_INTEGER, 0, 0, &u_time.tm, 0, &resUTimeSec);
    // check sqlRes - always OK
    .........
    sqlRes = SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_SLONG,
          SQL_INTEGER, 0, 0, &u_time.cnt, 0, &resUTimeMSec);
    // check sqlRes - always OK
    .........
    sqlRes = SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_SLONG,
          SQL_INTEGER, 0, 0, &commonLen, 0, &resDataLen);
    // check sqlRes - always OK
    .........
    sqlRes = SQLBindParameter(stmt, POS_BLOB_DATA, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, 0, 0, dynData ? (SQLPOINTER) POS_BLOB_DATA : pdata, 0, &resData);
    // check sqlRes - always OK

    sqlRes = SQLExecute(stmt);

    if(sqlRes == SQL_NEED_DATA)
    {
      // this code executing when dynData == 1

      int parameter;
      if(SQLParamData(stmt,(void**)&parameter) == SQL_NEED_DATA
         && parameter == POS_BLOB_DATA)
      {
        int len = 0;
        int partsize;

        // storing long data by parts
        while (len < commonLen)
        {
          partsize = commonLen - len;
          if(partsize > MAX_PART_SIZE) partsize = MAX_PART_SIZE;
          sqlRes = SQLPutData(stmt, buffer+len, partsize);
          if(sqlRes == SQL_ERROR) return ...; // Always OK, not SQL_ERROR!
          len += partsize;
        }
        if(SQLParamData(stmt,(void**)&parameter) == SQL_ERROR)
           PrintDiagCode(); // Print state and error text
            // This is printed ERROR if columns (1,2,3,4):
            // id , u_time_sec  , u_time_msec , pdata_len
            // are created with flag 'not NULL'

            // Another, if 'NULL' is permited,
            // this parameters take value NULL, blob pdata is correctly stored
            // and SQLParamData() return SQL_SUCCESS!
            // But I not wanted 'NULL', I setted values.
      }
    } // end if(sqlRes == SQL_NEED_DATA)
    
    else if(sqlRes == SQL_SUCCESS)
    {
      // this code executing when dynData == 0
      // all data always correctly stored
     .......

Suggested fix:
Check it.
[17 Sep 2003 1:18] Lev Kadisov
Previous and this code (SQLPutData) works with Borland Firebird 1.5 without any problem! 
But with MySQL and MyODBC exist small problem.

If I'am dynamic binding all parameters by next code.

    const int POS_ID =1;
    const int POS_TIME_SEC = 2;
    const int POS_TIME_MSEC = 3;
    const int POS_BLOBLEN = 4;
    const int POS_BLOB_DATA = 5;

    resId = dynData ? SQL_LEN_DATA_AT_EXEC(0) : 0;
    resUTimeSec = dynData ? SQL_LEN_DATA_AT_EXEC(0) : 0;
    resUTimeMSec = dynData ? SQL_LEN_DATA_AT_EXEC(0) : 0;
    resDataLen = dynData ? SQL_LEN_DATA_AT_EXEC(0) : 0;
    resData = dynData ? SQL_LEN_DATA_AT_EXEC(0) : datalen;

    SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_SLONG,
          SQL_INTEGER, 0, 0, dynData ? (SQLPOINTER) POS_ID : &id, 0, &resId);
    SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_SLONG,
          SQL_INTEGER, 0, 0,
          dynData ? (SQLPOINTER) POS_TIME_SEC : &u_time.tm, 0, &resUTimeSec);
    SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_SLONG,
          SQL_INTEGER, 0, 0, 
          dynData ? (SQLPOINTER) POS_TIME_MSEC : &u_time.cnt, 0, &resUTimeMSec);
    SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_SLONG,
          SQL_INTEGER, 0, 0, 
          dynData ? (SQLPOINTER) POS_BLOBLEN : &commonLen, 0, &resDataLen);

    SQLBindParameter(stmt, POS_BLOB_DATA, SQL_PARAM_INPUT,
                                    SQL_C_BINARY,
                                    SQL_LONGVARBINARY, 0, 0, 
                                    dynData ? 
                                       (SQLPOINTER) POS_BLOB_DATA 
                                     : pdata, 
                                    0, &resData);

then 
exception exist at address 01556cc3
at last call SQLParamData() for parameter 5 (POS_BLOB_DATA) after all SQLParamData() and SQLPutData() for parameters 1,2,3,4 in myodbc3.dll.

registers:
 EAX = 0065FA42 EBX = 016D1F00
 ECX = 0000000C EDX = 00000006
 ESI = 00000001 EDI = 015C8A4C
 EIP = 01556CC3 ESP = 0065F478
 EBP = 015C8A4C EFL = 00010246
 CS = 018F DS = 0197 ES = 0197
 SS = 0197 FS = 8A8F GS = 0000 OV=0
 UP=0 EI=1 PL=0 ZR=1 AC=0 PE=1 CY=0

 00000001 = ????????

 ST0 = +0.00000000000000000e+0000
 ST1 = +0.00000000000000000e+0000
 ST2 = +0.00000000000000000e+0000
 ST3 = +0.00000000000000000e+0000
 ST4 = +0.00000000000000000e+0000
 ST5 = +0.00000000000000000e+0000
 ST6 = +0.00000000000000000e+0000
 ST7 = +1.00000000000000000e+0000
 CTRL = 027F STAT = 0020 TAGS = FFFF
 EIP = 7FC348FA CS = 018F DS = 0197
 EDO = 0065D354

01556BF9   push        4
01556BFB   push        157F0E0h
01556C00   push        edx
01556C01   push        eax
01556C02   call        01556960
01556C07   add         esp,10h
01556C0A   pop         edi
01556C0B   pop         esi
01556C0C   pop         ebp
01556C0D   pop         ebx
01556C0E   add         esp,80h
01556C14   ret
01556C15   mov         esi,dword ptr [ebp+4]
01556C18   test        esi,esi
01556C1A   je          01556C3F
01556C1C   mov         eax,dword ptr [ebp+10h]
01556C1F   test        eax,eax
01556C21   je          01556C2F
01556C23   push        eax
01556C24   push        esi
01556C25   call        01562E40
01556C2A   add         esp,8
01556C2D   jmp         01556C41
01556C2F   or          ecx,0FFh
01556C32   xor         eax,eax
01556C34   mov         edi,esi
01556C36   repne scas  byte ptr [edi]
01556C38   not         ecx
01556C3A   dec         ecx
01556C3B   mov         eax,ecx
01556C3D   jmp         01556C41
01556C3F   xor         eax,eax
01556C41   movsx       ecx,word ptr [ebp+2]
01556C45   add         ecx,1Ch
01556C48   cmp         ecx,79h
01556C4B   ja          01556E01
01556C51   xor         edx,edx
01556C53   mov         dl,byte ptr [ecx+1557018h]
01556C59   jmp         dword ptr [edx*4+1556FDCh]
01556C60   mov         bl,1
01556C62   jmp         01556E01
01556C67   movsx       ecx,byte ptr [esi]
01556C6A   push        0F6h
01556C6C   lea         eax,[esp+14h]
01556C70   push        eax
01556C71   push        ecx
01556C72   call        015619A0
01556C77   add         esp,0Ch
01556C7A   jmp         01556D14
01556C7F   xor         ecx,ecx
01556C81   mov         cl,byte ptr [esi]
01556C83   push        0F6h
01556C85   lea         eax,[esp+14h]
01556C89   push        eax
01556C8A   push        ecx
01556C8B   call        015619A0
01556C90   add         esp,0Ch
01556C93   jmp         01556D14
01556C95   movsx       ecx,word ptr [esi]
01556C98   push        0F6h
01556C9A   lea         eax,[esp+14h]
01556C9E   push        eax
01556C9F   push        ecx
01556CA0   call        015619A0
01556CA5   add         esp,0Ch
01556CAF   push        0F6h
01556CB1   lea         eax,[esp+14h]
01556CB5   push        eax
01556CB6   push        ecx
01556CB7   call        015619A0
01556CBC   add         esp,0Ch
01556CBF   jmp         01556D14
01556CC1   push        0F6h
01556CC3   mov         ecx,dword ptr [esi]    // this is point of exception
01556CC5   lea         eax,[esp+14h]
01556CC9   push        eax
01556CCA   push        ecx
01556CCB   call        015619A0
01556CD0   add         esp,0Ch
01556CD3   jmp         01556D14
01556CD5   push        0Ah
01556CD7   jmp         01556CC3
01556CD9   mov         ecx,dword ptr [esi+4]
01556CDC   mov         edx,dword ptr [esi]
01556CDE   push        0F6h
01556CE0   lea         eax,[esp+14h]
01556CE4   push        eax
01556CE5   push        ecx
01556CE6   push        edx
01556CE7   call        01562D20
01556CEC   lea         ecx,[esp+20h]
01556CF0   add         esp,10h
01556CF3   sub         eax,ecx
01556CF5   lea         esi,[esp+10h]
01556CF9   jmp         01556E01
01556CFE   mov         eax,dword ptr [esi+4]
01556D01   mov         ecx,dword ptr [esi]
01556D03   push        0Ah
01556D05   lea         edx,[esp+14h]
01556D09   push        edx
01556D0A   push        eax
01556D0B   push        ecx
01556D0C   call        01562D20
01556D11   add         esp,10h
01556D14   lea         edx,[esp+10h]
[17 Sep 2003 1:42] Lev Kadisov
OS is Windows98, but not 95.
[6 Oct 2003 0:20] Venu Anuganti
Hi !!

I tried to reproduce this, but no luck.. it just works fine at my end.. Here is a simple test and its output

/*
  Test for a simple SQLPutData and SQLParamData handling 
  bug #1316
*/

static void t_putdata3(SQLHDBC hdbc, SQLHSTMT hstmt)
{
  char buffer[]= "MySQL - The worlds's most popular open source database";
  SQLRETURN  rc;  
  const int MAX_PART_SIZE = 5;

  char *pdata= 0, data[50];
  int dynData;
  int commonLen= 20;

  SQLINTEGER  id, id1, id2, id3, resId;
  SQLINTEGER  resUTimeSec;
  SQLINTEGER  resUTimeMSec;
  SQLINTEGER  resDataLen;
  SQLINTEGER  resData;

  myheader("t_putdata3");
    

    SQLExecDirect(hstmt,"drop table t_putdata3",SQL_NTS);
    rc = SQLExecDirect(hstmt,"CREATE TABLE t_putdata3 ( id INT, id1  INT, \
                     id2 INT, id3  INT, pdata blob);",SQL_NTS);
    mystmt(hstmt,rc);

    dynData = 1; 

    rc = SQLPrepare(hstmt, "INSERT INTO t_putdata3 VALUES ( ?, ?, ?, ?, ? )", SQL_NTS);
    mystmt(hstmt,rc);

    id= 1, id1= 2, id2= 3, id3= 4;
    resId = 0;
    resUTimeSec = 0;
    resUTimeMSec = 0;
    resDataLen = 0;
    resData = SQL_LEN_DATA_AT_EXEC(0);

    rc = SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_SLONG,
                          SQL_INTEGER, 0, 0, &id, 0, &resId);
    mystmt(hstmt,rc);

    rc = SQLBindParameter(hstmt, 2, SQL_PARAM_INPUT, SQL_C_SLONG,
                          SQL_INTEGER, 0, 0, &id1, 0, &resUTimeSec);
    mystmt(hstmt,rc);

    rc = SQLBindParameter(hstmt, 3, SQL_PARAM_INPUT, SQL_C_SLONG,
                          SQL_INTEGER, 0, 0, &id2, 0, &resUTimeMSec);
    mystmt(hstmt,rc);

    rc = SQLBindParameter(hstmt, 4, SQL_PARAM_INPUT, SQL_C_SLONG,
                          SQL_INTEGER, 0, 0, &id3, 0, 
                          &resDataLen);
    mystmt(hstmt,rc);

    rc = SQLBindParameter(hstmt, 5, SQL_PARAM_INPUT,
                          SQL_C_BINARY, SQL_LONGVARBINARY, 10, 10, 
                          dynData ? (SQLPOINTER)5 :
                          pdata, 0, &resData);
    mystmt(hstmt,rc);

    rc = SQLExecute(hstmt);
    if (rc == SQL_NEED_DATA)
    {
      int parameter;
      if (SQLParamData(hstmt,(void**)&parameter) == SQL_NEED_DATA && parameter == 5)
      {
        int len = 0;
        int partsize;

        /* storing long data by parts */
        while (len < commonLen)
        {
          partsize = commonLen - len;
          if (partsize > MAX_PART_SIZE) 
            partsize = MAX_PART_SIZE;
          
          rc = SQLPutData(hstmt, buffer+len, partsize);
          mystmt(hstmt,rc);
          len += partsize;
        }
        if (SQLParamData(hstmt,(void**)&parameter) == SQL_ERROR)
        {
           
        }
      }
    } /* end if (rc == SQL_NEED_DATA) */

    SQLFreeStmt(hstmt, SQL_UNBIND);
    SQLFreeStmt(hstmt, SQL_CLOSE);

    rc = tmysql_exec(hstmt,"select id, id1, id2, id3,  convert(pdata,char) from t_putdata3");
    mystmt(hstmt,rc);  

    rc = SQLFetch(hstmt);

    my_assert(1 == my_fetch_int(hstmt,1));
    my_assert(2 == my_fetch_int(hstmt,2));
    my_assert(3 == my_fetch_int(hstmt,3));
    my_assert(4 == my_fetch_int(hstmt,4));

    my_assert(strncmp(buffer, my_fetch_str(hstmt,data,5), commonLen) == 0);

    rc = SQLFreeStmt(hstmt,SQL_CLOSE);
    mystmt(hstmt,rc);
}

     output:

      ######################################
      t_putdata3
      ######################################
       my_fetch_int: 1
       my_fetch_int: 2
       my_fetch_int: 3
       my_fetch_int: 4
       my_fetch_str: MySQL - The worlds's(20)

Thanks
[13 Oct 2003 3:55] Lev Kadisov
Hi!
Sorry for my careless mistake.
I not used functions after SQLFetch()
    SQLFreeStmt(hstmt, SQL_UNBIND);
    SQLFreeStmt(hstmt, SQL_CLOSE);
Now, all Ok.
Thanks.