Bug #48699 c/ODBC cuts BINARY parameter after encountering first 00
Submitted: 11 Nov 2009 20:10 Modified: 19 Nov 2009 15:15
Reporter: Tonci Grgin Email Updates:
Status: Not a Bug Impact on me:
None 
Category:Connector / ODBC Severity:S2 (Serious)
Version:all OS:Any
Assigned to: Lawrenty Novitsky CPU Architecture:Any
Triage: D2 (Serious)

[11 Nov 2009 20:10] Tonci Grgin
Description:
Using following test case one can see in server log that only the value #80 gets inserted:

	CHAR buf[5];
	SQLINTEGER len;
	buf[0] = '\128';
	buf[1] = '\00';
	buf[2] = '\CE';
	buf[3] = '\C4';
	buf[4] = '\88';
	len= strlen(buf);
	buf[5]= 0;

	SQLRETURN rc;
	SQLHENV  henv;
	SQLHDBC  hdbc;
	SQLHSTMT hstmt;

    rc = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);
    if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO)
		return(-1);

    rc = SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, SQL_NTS);
    if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO)
		return(-1);
        
    rc = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
    if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO)
		return(-1);
        
    std::cout << "Connecting to " << "5-1-5-on-opensol" << std::endl;

    rc = SQLConnect(hdbc, (SQLCHAR *)"5-1-5-on-opensol", SQL_NTS, 
                  (SQLCHAR *)"root", SQL_NTS, 
                  (SQLCHAR *)"***", SQL_NTS);
    if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO)
		return(-1);
        
    rc = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
    if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO)
		return(-1);
 
    rc=SQLExecDirect(hstmt, (SQLCHAR *)"drop table if exists issuexx", SQL_NTS);
    rc=SQLExecDirect(hstmt, (SQLCHAR *)"create table issuexx (id integer NOT NULL PRIMARY KEY, obj BINARY(5)) DEFAULT CHARSET UTF8", SQL_NTS);
    if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO)
		return(-1);

    rc = (hstmt, SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_BINARY,
		SQL_BINARY, 5, 0, buf, 0, &len));//SQL_LONGVARCHAR  SQL_WCHAR
    if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO)
		return(-1);

    rc = SQLExecDirect(hstmt, (SQLCHAR *)"insert into issuexx(id, obj) values(1, ?)", SQL_NTS);
    if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO)
		return(-1);

    std::cout << "Success:" << "test done" << std::endl;
    return 0;

How to repeat:
See above.

Further more, if first byte is "00" the execution will break with error in syntax.

Suggested fix:
-
[13 Nov 2009 8:32] Tonci Grgin
Bug#48752 was marked as duplicate of this report.
[13 Nov 2009 9:54] Bogdan Degtyariov
patch and test case

Attachment: patch48699.diff (text/x-diff), 2.96 KiB.

[13 Nov 2009 9:56] Bogdan Degtyariov
The initial test case does not reproduce the problem because it does not use row-wise parameter binding. Please see the test case in the attached file.
[13 Nov 2009 16:20] Lawrenty Novitsky
If we agree this is not a bug, and i tend to think so, the outcome of this bug should be following patch:
=== modified file 'driver/execute.c'
--- driver/execute.c  2009-10-07 20:43:23 +0000
+++ driver/execute.c  2009-11-13 16:15:14 +0000
@@ -276,13 +276,20 @@
       *toptr= add_to_buffer(net,*toptr,"NULL",4);
       return SQL_SUCCESS;
     }
+    /*
+      According to http://msdn.microsoft.com/en-us/library/ms710963%28VS.85%29.aspx
+      
+      "... If StrLen_or_IndPtr is a null pointer, the driver assumes that all^M
+      input parameter values are non-NULL and that character and *binary* data
+      is null-terminated."
+    */
     else if (!octet_length_ptr || *octet_length_ptr == SQL_NTS)
     {
       if (data)
       {
         if (aprec->concise_type == SQL_C_WCHAR)
           length= sqlwcharlen((SQLWCHAR *)data) * sizeof(SQLWCHAR);
-        else /* TODO this is stupid, check condition above, shouldn't we be checking only octet_length, not ptr? */
+        else
           length= strlen(data);

         if (!octet_length_ptr && aprec->octet_length > 0 &&

Because seems like quote from msdn answers the question in the comment this patch removes.
[16 Nov 2009 4:08] Bogdan Degtyariov
Lawrin,

thank you. Confirmed as not a bug.
[19 Nov 2009 7:57] Tonci Grgin
Lawrin, Bogdan:

We should check on this again... The above msdn link quote is only applied to null pointer value for StrLen_or_IndPtr parameter for SQLBindParameter. But not for when one calls SQLBinParameter for SQL_C_BINARY type, passing the number of bytes in C binary structure and not a null pointer for StrLen_or_IndPtr. As you see in my example, the value for input parameter StrLen_or_IndPtr for SQLBindParameter is len= strlen(buf); Following quote from MSDN should apply: http://msdn.microsoft.com/en-us/library/ms710963%28VS.85%29.aspx:

StrLen_or_IndPtr Argument

The StrLen_or_IndPtr argument points to a buffer that, when SQLExecute or SQLExecDirect is called, contains one of the following. (This argument sets the SQL_DESC_OCTET_LENGTH_PTR and SQL_DESC_INDICATOR_PTR record fields of the application parameter pointers.)

    * The length of the parameter value stored in *ParameterValuePtr. This is ignored except for character or binary C data.
    * SQL_NTS. The parameter value is a null-terminated string.

Lawrentiy, please recheck your ruling.
[19 Nov 2009 15:15] Lawrenty Novitsky
If to change in Tonci's example len= strlen() to len= 5, everything is gonna work just fine.
strlen won't work here, because it stops counting bytes on first 0 byte. You can test the value - it will be 1.
Value pointed by StrLen_Or_IndicatorPtr is used as number of bytes to copy from binary parameter. I believe that is in full accordance with the given quote from MSDN.