Bug #115584 Issues with bound parameters
Submitted: 13 Jul 12:04 Modified: 13 Aug 19:17
Reporter: Jon Bird Email Updates:
Status: Closed Impact on me:
None 
Category:Connector / ODBC Severity:S2 (Serious)
Version:8.4, 9.0 OS:Windows (Win64)
Assigned to: CPU Architecture:x86

[13 Jul 12:04] Jon Bird
Description:
I am seeing (in some cases, sporadic) issues using bound parameters with the above ODBC connector.

Here is the query:

INSERT INTO PlayListNames ( Description, IncludeInLists,   VoiceTrack, ScheduleDate, ScheduleTime, AutoSchedule, AutoSequence,   AutoSeqPrimaryPlayListId, AutoSeqBackupPlayListId, ScheduleDays, ResetListOnSchedule,   AutoSeqPrimaryLoop, AutoSeqBackupLoop, CloudUpload, WriteMetadataOnCloudUpload )   VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? );

There are 15 parameters, all bound correctly. Sporadically (say 1 in 8), this query will fail with the following error diagnostic:

[MySQL][ODBC 8.4(w) Driver][mysqld-8.0.37-0ubuntu0.22.04.3]Using unsupported buffer type: %d  (parameter: %d) 

However a subsequent re-execution of the same query (without touching the bindings) will normally then succeed.

A second instance (which I think may be the same problem) but which always triggers this problem is if I run the following query:

SELECT PlayListNames.PlayListId, PlayListNames.Description, PlayListNames.IncludeInLists, PlayListNames.VoiceTrack, PlayListNames.ScheduleDate, PlayListNames.ScheduleTime, PlayListNames.AutoSchedule, PlayListNames.AutoSequence, PlayListNames.AutoSeqPrimaryPlayListId ,PlayListNames.AutoSeqBackupPlayListId, PlayListNames.ScheduleDays, PlayListNames.ResetListOnSchedule, PlayListNames.AutoSeqPrimaryLoop, PlayListNames.AutoSeqBackupLoop, PlayListNames.CloudUpload, PlayListNames.WriteMetadataOnCloudUpload FROM PlayListNames WHERE (((PlayListNames.Description)=?));

But with the same set of bound parameters ie. I've bound 15 but only 1 is needed. I think this is a legitimate thing to do, certainly I've seen no problems in the past with various ODBC connectors as in I would expect the remaining params to be ignored. Regardless, this will always trigger the above failure yet succeed if retried.

How to repeat:
Can be consistently repeated using a query which binds more parameters than are in the SQL.

Suggested fix:
I think this issue (in the same as #115531) may have been triggered by WL #15967 but there may also be something not quite right with the 8.3 connector as well.

In all cases, the error appears to be triggered by this procedure failing:

      bind_failed =
        mysql_stmt_bind_named_param(ssps, param_bind.data(),
                                    p_number, query_attr_names.data());

p_number is set just prior to this call as follows:

    unsigned int p_number =
      dbc->mysql->server_capabilities & CLIENT_QUERY_ATTRIBUTES
        ? query_attr_names.size() : param_count;

With an 8.0x server, I would expect it to be set to query_attr_names.size(), which appears to be the case.

With the INSERT query, the first time the query is executed, the (I think) pertinent variables are:

query_attr_names.size()	18	
param_count	15	
p_number	18	

However on the 2nd call they are:

query_attr_names.size()	16
param_count	15	
p_number	16	

So there is a discrepancy going on there. Of interest, there is also a slight discrepancy going on with the 8.3 connector:

First run:

p_number	16	
stmt->query_attr_names.size()	16	
stmt->param_count	15	

Second run:

p_number	15
stmt->query_attr_names.size()	15
stmt->param_count	15	

I've not seen a problem with 8.3 however it does trigger a runtime error (which can be ignored) with the debug builds of the connector on the first run. This I think is down to a memcpy attempting to copy 0 bytes to a NULL pointer - possibly that 16th (uninitialized?) parameter.

With the SELECT query, the difference between the two calls is more pronounced:

First run:

query_attr_names.size()	32	
param_count	1	
p_number	32	

Second run:
query_attr_names.size()	16	
param_count	1	
p_number	16	

With the 8.3 connector, this consistently uses what appear (to me anyway), sensible values:

p_number	1	
stmt->query_attr_names.size()	1	
stmt->param_count	1	

To me it looks like the parameter count being passed into the SQL client library is over that actually supplied which means accessing data beyond the end of the supplied array. As a result, you then get inconsistent behavior depending on what sits in memory after that. Odds are just being one over tends to be ok but by a factor of two it's almost certain to trigger a problem.
[15 Jul 12:39] MySQL Verification Team
Hello Jon,

Thank you for the bug report and feedback.

Regards,
Ashwini Patil
[22 Jul 8:56] Bogdan Degtyariov
Posted by developer:
 
The problem in Connector/ODBC driver has been identified.
We are working on fixing it in the code.
[7 Aug 9:54] Bogdan Degtyariov
Posted by developer:
 
The problem happened when the number of bound parameters was greater than the number of placeholders in the corresponding SQL query. The statement had to be prepared and executed more than once.
The fix for the issue was pushed into the source tree along with the unit test.
[13 Aug 17:20] Philip Olson
Posted by developer:
 
Fixed as of the upcoming MySQL Connector/ODBC 9.1.0 release, and here's the proposed changelog entry from the documentation team:

Having a number of bound parameters greater than the number of
placeholders in the corresponding SQL query could emit an error, as the
statement had to be prepared and executed more than once.

Thank you for the bug report.
[13 Aug 19:17] Jon Bird
Quick query: I get the explainer for why the SELECT query fails but this doesn't appear to correlate as to why, sporadically the INSERT query (where the bound parameters does match the placeholders) will fail with an error.