| Bug #5254 | Prepared Statement C API does not work | ||
|---|---|---|---|
| Submitted: | 27 Aug 2004 15:13 | Modified: | 7 Sep 2004 15:48 |
| Reporter: | Rick Robinson | Email Updates: | |
| Status: | Not a Bug | Impact on me: | |
| Category: | MySQL Server | Severity: | S2 (Serious) |
| Version: | 4.1.3 beta | OS: | Solaris (Solaris 9 Sparc) |
| Assigned to: | Konstantin Osipov | CPU Architecture: | Any |
[30 Aug 2004 20:02]
Hartmut Holzgraefe
Verified with gcc 3.3.1 (SuSE 9.0). Happens with libmysqlclient_r while regular libmysqlclient works fine.
[30 Aug 2004 22:50]
Rick Robinson
I see that the problem was replicated - but it begs the question; is this a problem with just prepared statements and the reentrant lib or should I be concerned about using the reentrant lib with 4.1.3 for anything else? Thx, R
[7 Sep 2004 15:48]
Konstantin Osipov
Hello Rick.
After fixing a few bugs in your test case, I wasn't able to repeat the bug.
Both, libmysqlclient_r and libmysqlclient work fine.
In contrary, if I try the original test case, it fails, no matter how it's compiled.
See the modified test case below. You shouldn't have set bind[i].length to point
to uninitialized lengths - on input, bind[i].length should be set only if length varies
from execute to execute. If this is the case, then you can reset lengths
before calling mysql_stmt_execute, without having to rebind parameters each time.
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include "mysql.h"
using namespace std;
int main(int argc, char *argv[])
{
cout << "start up" << endl;
MYSQL * connection;
connection = mysql_init(0);
// update for your own environment
connection = mysql_real_connect(connection, 0, "root", "", "test", 0,
"/opt/local/var/mysql/mysql.sock", 0);
if (0 == connection)
{
cerr << "connect failed:" << mysql_error(connection) << endl;
return -1;
}
static const char * sql_statement =
"select property_value from cfg_2 "
"where config_key=? and section_key=? and property_key=?";
MYSQL_STMT * stmt_handle = mysql_stmt_init(connection);
if (0 == stmt_handle)
{
cerr << "stmt init failed:" << mysql_error(connection) << endl;
return -1;
}
int mysql_return_code = mysql_stmt_prepare(stmt_handle,
sql_statement,
strlen(sql_statement)
);
if (0 != mysql_return_code)
{
cerr << "stmt prepare failed:" << mysql_stmt_error(stmt_handle) <<
endl;
mysql_stmt_close(stmt_handle);
return -1;
}
MYSQL_BIND bind_var[3];
unsigned long var_length[3];
my_bool not_null = 0;
char config_key[31] = {'\0'};
char section_key[31] = {'\0'};
char property_key[256] = {'\0'};
bzero(bind_var, sizeof(bind_var));
bind_var[0].buffer_type = MYSQL_TYPE_VAR_STRING;
bind_var[0].buffer = (char *)config_key;
bind_var[0].buffer_length = sizeof(config_key);
bind_var[0].is_null = ¬_null;
bind_var[1].buffer_type = MYSQL_TYPE_VAR_STRING;
bind_var[1].buffer = (char *)section_key;
bind_var[1].buffer_length = sizeof(section_key);
bind_var[1].is_null = ¬_null;
bind_var[2].buffer_type = MYSQL_TYPE_VAR_STRING;
bind_var[2].buffer = (char *)property_key;
bind_var[2].buffer_length = sizeof(property_key);
bind_var[2].is_null = ¬_null;
strncpy(config_key, "svp001", sizeof(section_key)-1);
strncpy(section_key, "stats", sizeof(section_key)-1);
strncpy(property_key, "number.of.threads", sizeof(property_key)-1);
mysql_return_code = mysql_stmt_bind_param(stmt_handle, bind_var);
if (0 != mysql_return_code)
{
cerr << "stmt bind failed:" << mysql_stmt_error(stmt_handle) << endl;
mysql_stmt_close(stmt_handle);
return -1;
}
mysql_return_code = mysql_stmt_execute(stmt_handle);
if (0 != mysql_return_code)
{
cerr << "stmt execute failed:" << mysql_stmt_error(stmt_handle) <<
endl;
mysql_stmt_close(stmt_handle);
return -1;
}
char property_value[256] = {'\0'};
MYSQL_BIND result_var[1];
unsigned long result_var_length[1];
my_bool is_null[1];
bzero(result_var, sizeof(result_var));
result_var[0].buffer_type = MYSQL_TYPE_VAR_STRING;
result_var[0].buffer = (char *)property_value;
result_var[0].buffer_length = sizeof(property_value)-1;
result_var[0].is_null = &is_null[0];
result_var[0].length = &result_var_length[0];
mysql_return_code = mysql_stmt_bind_result(stmt_handle, result_var);
if (0 != mysql_return_code)
{
cerr << "stmt bind rslt failed:" << mysql_stmt_error(stmt_handle) <<
endl;
mysql_stmt_close(stmt_handle);
return -1;
}
mysql_return_code = mysql_stmt_store_result(stmt_handle);
if (0 != mysql_return_code)
{
cerr << "stmt store rslt failed:" << mysql_stmt_error(stmt_handle) <<
endl;
mysql_stmt_close(stmt_handle);
return -1;
}
mysql_return_code = mysql_stmt_fetch(stmt_handle);
if (0 != mysql_return_code && MYSQL_NO_DATA != mysql_return_code)
{
cerr << "stmt fetch failed:" << mysql_stmt_error(stmt_handle) << endl;
mysql_stmt_free_result(stmt_handle);
mysql_stmt_close(stmt_handle);
return -1;
}
if (MYSQL_NO_DATA != mysql_return_code)
{
cout << "stmt - got: " << property_value << endl;
}
mysql_stmt_free_result(stmt_handle);
mysql_stmt_close(stmt_handle);
mysql_close(connection);
mysql_server_end();
cout << "done" << endl;
return 0;
}
Note, that the sample is still wrong, as either buffer_length or length should reflect
real lengths of values, not lengths of buffers.
kostja@oak:~/work/mysql-4.1-5254/tests> g++ 5254.cc -I../include -L../libmysql/.libs/ -lmysqlclient -lz
kostja@oak:~/work/mysql-4.1-5254/tests> ./5254
start up
done
kostja@oak:~/work/mysql-4.1-5254/tests>

Description: The prepared statement C API does not work for simple select. The code listed below shows the example. The code listed below receives an error "MySQL client run out of memory" - but should just get a one row result. In other tests (I have not listed code here), a very similar example receives a core dump. Some of the relevant details: Solaris 9 Sparc, gcc 3.3.2, using 32 bit, max binary version of 4.1.3 beta MySQL for Solaris 9, linked with reentrant client lib (libmyclient_r), using a my-medium.cnf config. How to repeat: create table cfg_2 (config_key varchar(30) not null, section_key varchar(30) not null, property_key varchar(255) not null, property_value varchar(255), primary key(config_key, section_key, property_key) ) engine=myisam; insert into cfg_2 values('svp001','stats','number.of.threads','2'); <code> #include <iostream> #include <cstdlib> #include <cstring> #include <cstdio> #include "mysql.h" using namespace std; int main(int argc, char *argv[]) { cout << "start up" << endl; MYSQL * connection; connection = mysql_init(0); // update for your own environment connection = mysql_real_connect(connection, 0, "sfwd", "sfwd", "sfwd", 0, 0,0); if (0 == connection) { cerr << "connect failed:" << mysql_error(connection) << endl; return -1; } static const char * sql_statement = "select property_value from cfg_2 " "where config_key=? and section_key=? and property_key=?"; MYSQL_STMT * stmt_handle = mysql_stmt_init(connection); if (0 == stmt_handle) { cerr << "stmt init failed:" << mysql_error(connection) << endl; return -1; } int mysql_return_code = mysql_stmt_prepare(stmt_handle, sql_statement, strlen(sql_statement) ); if (0 != mysql_return_code) { cerr << "stmt prepare failed:" << mysql_stmt_error(stmt_handle) << endl; mysql_stmt_close(stmt_handle); return -1; } MYSQL_BIND bind_var[3]; unsigned long var_length[3]; my_bool not_null = 0; char config_key[31] = {'\0'}; char section_key[31] = {'\0'}; char property_key[256] = {'\0'}; bind_var[0].buffer_type = MYSQL_TYPE_VAR_STRING; bind_var[0].buffer = (char *)config_key; bind_var[0].buffer_length = sizeof(config_key); bind_var[0].length = &var_length[0]; bind_var[0].is_null = ¬_null; bind_var[1].buffer_type = MYSQL_TYPE_VAR_STRING; bind_var[1].buffer = (char *)section_key; bind_var[1].buffer_length = sizeof(section_key); bind_var[1].length = &var_length[1]; bind_var[1].is_null = ¬_null; bind_var[2].buffer_type = MYSQL_TYPE_VAR_STRING; bind_var[2].buffer = (char *)property_key; bind_var[2].buffer_length = sizeof(property_key); bind_var[2].length = &var_length[2]; bind_var[2].is_null = ¬_null; strncpy(config_key, "svp001", sizeof(section_key)-1); strncpy(section_key, "stats", sizeof(section_key)-1); strncpy(property_key, "number.of.threads", sizeof(property_key)-1); mysql_return_code = mysql_stmt_bind_param(stmt_handle, bind_var); if (0 != mysql_return_code) { cerr << "stmt bind failed:" << mysql_stmt_error(stmt_handle) << endl; mysql_stmt_close(stmt_handle); return -1; } mysql_return_code = mysql_stmt_execute(stmt_handle); if (0 != mysql_return_code) { cerr << "stmt execute failed:" << mysql_stmt_error(stmt_handle) << endl; mysql_stmt_close(stmt_handle); return -1; } char property_value[256] = {'\0'}; MYSQL_BIND result_var[1]; unsigned long result_var_length[1]; my_bool is_null[1]; result_var[0].buffer_type = MYSQL_TYPE_VAR_STRING; result_var[0].buffer = (char *)property_value; result_var[0].buffer_length = sizeof(property_value)-1; result_var[0].is_null = &is_null[0]; result_var[0].length = &result_var_length[0]; mysql_return_code = mysql_stmt_bind_result(stmt_handle, result_var); if (0 != mysql_return_code) { cerr << "stmt bind rslt failed:" << mysql_stmt_error(stmt_handle) << endl; mysql_stmt_close(stmt_handle); return -1; } mysql_return_code = mysql_stmt_store_result(stmt_handle); if (0 != mysql_return_code) { cerr << "stmt store rslt failed:" << mysql_stmt_error(stmt_handle) << endl; mysql_stmt_close(stmt_handle); return -1; } mysql_return_code = mysql_stmt_fetch(stmt_handle); if (0 != mysql_return_code && MYSQL_NO_DATA != mysql_return_code) { cerr << "stmt fetch failed:" << mysql_stmt_error(stmt_handle) << endl; mysql_stmt_free_result(stmt_handle); mysql_stmt_close(stmt_handle); return -1; } if (MYSQL_NO_DATA != mysql_return_code) { cout << "stmt - got: " << property_value << endl; } mysql_stmt_free_result(stmt_handle); mysql_stmt_close(stmt_handle); cout << "done" << endl; return 0; } </code> Suggested fix: Review the prepared statement API code, fix the prepared statement API code, and improve the doc for prepared statement C API usage.