mirror of
https://github.com/MariaDB/server.git
synced 2025-07-30 16:24:05 +03:00
A fix and test case for bug#6059 "mysql_stmt_field_count returns
positive numbers when no resultset is available": when sending result set metadata we need to use virtual select_result::send_fields, and not address protocol directly, because select_result descendents may intercept result set (it's the case for example for SELECT INTO OUTFILE). sql/sql_class.h: A fix for bug#6059 "mysql_stmt_field_count returns positive numbers when no resultset is available": introducing select_result::field_count() method to report actual number of fields in a result set, if any result set is sent to client. sql/sql_prepare.cc: A fix for bug#6059 "mysql_stmt_field_count returns positive numbers when no resultset is available": when sending result set metadata we need to use virtual select_result::send_fields, and not address protocol directly, because select_result descendents may intercept result set (it's the case for example for SELECT INTO OUTFILE). Now we need to always have lex->result set if we're in prepared statements. tests/client_test.c: A test case for Bug#6059 "mysql_stmt_field_count returns positive numbers when no resultset is available"
This commit is contained in:
@ -1109,6 +1109,13 @@ public:
|
|||||||
unit= u;
|
unit= u;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
Because of peculiarities of prepared statements protocol
|
||||||
|
we need to know number of columns in the result set (if
|
||||||
|
there is a result set) apart from sending columns metadata.
|
||||||
|
*/
|
||||||
|
virtual uint field_count(List<Item> &fields) const
|
||||||
|
{ return fields.elements; }
|
||||||
virtual bool send_fields(List<Item> &list,uint flag)=0;
|
virtual bool send_fields(List<Item> &list,uint flag)=0;
|
||||||
virtual bool send_data(List<Item> &items)=0;
|
virtual bool send_data(List<Item> &items)=0;
|
||||||
virtual bool initialize_tables (JOIN *join=0) { return 0; }
|
virtual bool initialize_tables (JOIN *join=0) { return 0; }
|
||||||
@ -1123,6 +1130,20 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Base class for select_result descendands which intercept and
|
||||||
|
transform result set rows. As the rows are not sent to the client,
|
||||||
|
sending of result set metadata should be suppressed as well.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class select_result_interceptor: public select_result
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
uint field_count(List<Item> &fields) const { return 0; }
|
||||||
|
bool send_fields(List<Item> &fields, uint flag) { return FALSE; }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
class select_send :public select_result {
|
class select_send :public select_result {
|
||||||
public:
|
public:
|
||||||
select_send() {}
|
select_send() {}
|
||||||
@ -1132,7 +1153,7 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class select_to_file :public select_result {
|
class select_to_file :public select_result_interceptor {
|
||||||
protected:
|
protected:
|
||||||
sql_exchange *exchange;
|
sql_exchange *exchange;
|
||||||
File file;
|
File file;
|
||||||
@ -1144,7 +1165,6 @@ public:
|
|||||||
select_to_file(sql_exchange *ex) :exchange(ex), file(-1),row_count(0L)
|
select_to_file(sql_exchange *ex) :exchange(ex), file(-1),row_count(0L)
|
||||||
{ path[0]=0; }
|
{ path[0]=0; }
|
||||||
~select_to_file();
|
~select_to_file();
|
||||||
bool send_fields(List<Item> &list, uint flag) { return 0; }
|
|
||||||
void send_error(uint errcode,const char *err);
|
void send_error(uint errcode,const char *err);
|
||||||
bool send_eof();
|
bool send_eof();
|
||||||
void cleanup();
|
void cleanup();
|
||||||
@ -1171,7 +1191,7 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class select_insert :public select_result {
|
class select_insert :public select_result_interceptor {
|
||||||
public:
|
public:
|
||||||
TABLE *table;
|
TABLE *table;
|
||||||
List<Item> *fields;
|
List<Item> *fields;
|
||||||
@ -1187,8 +1207,6 @@ class select_insert :public select_result {
|
|||||||
}
|
}
|
||||||
~select_insert();
|
~select_insert();
|
||||||
int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
|
int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
|
||||||
bool send_fields(List<Item> &list, uint flag)
|
|
||||||
{ return 0; }
|
|
||||||
bool send_data(List<Item> &items);
|
bool send_data(List<Item> &items);
|
||||||
void send_error(uint errcode,const char *err);
|
void send_error(uint errcode,const char *err);
|
||||||
bool send_eof();
|
bool send_eof();
|
||||||
@ -1271,7 +1289,7 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class select_union :public select_result {
|
class select_union :public select_result_interceptor {
|
||||||
public:
|
public:
|
||||||
TABLE *table;
|
TABLE *table;
|
||||||
COPY_INFO info;
|
COPY_INFO info;
|
||||||
@ -1280,8 +1298,6 @@ class select_union :public select_result {
|
|||||||
select_union(TABLE *table_par);
|
select_union(TABLE *table_par);
|
||||||
~select_union();
|
~select_union();
|
||||||
int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
|
int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
|
||||||
bool send_fields(List<Item> &list, uint flag)
|
|
||||||
{ return 0; }
|
|
||||||
bool send_data(List<Item> &items);
|
bool send_data(List<Item> &items);
|
||||||
bool send_eof();
|
bool send_eof();
|
||||||
bool flush();
|
bool flush();
|
||||||
@ -1289,13 +1305,12 @@ class select_union :public select_result {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/* Base subselect interface class */
|
/* Base subselect interface class */
|
||||||
class select_subselect :public select_result
|
class select_subselect :public select_result_interceptor
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
Item_subselect *item;
|
Item_subselect *item;
|
||||||
public:
|
public:
|
||||||
select_subselect(Item_subselect *item);
|
select_subselect(Item_subselect *item);
|
||||||
bool send_fields(List<Item> &list, uint flag) { return 0; };
|
|
||||||
bool send_data(List<Item> &items)=0;
|
bool send_data(List<Item> &items)=0;
|
||||||
bool send_eof() { return 0; };
|
bool send_eof() { return 0; };
|
||||||
};
|
};
|
||||||
@ -1432,7 +1447,7 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class multi_delete :public select_result
|
class multi_delete :public select_result_interceptor
|
||||||
{
|
{
|
||||||
TABLE_LIST *delete_tables, *table_being_deleted;
|
TABLE_LIST *delete_tables, *table_being_deleted;
|
||||||
Unique **tempfiles;
|
Unique **tempfiles;
|
||||||
@ -1445,8 +1460,6 @@ public:
|
|||||||
multi_delete(THD *thd, TABLE_LIST *dt, uint num_of_tables);
|
multi_delete(THD *thd, TABLE_LIST *dt, uint num_of_tables);
|
||||||
~multi_delete();
|
~multi_delete();
|
||||||
int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
|
int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
|
||||||
bool send_fields(List<Item> &list,
|
|
||||||
uint flag) { return 0; }
|
|
||||||
bool send_data(List<Item> &items);
|
bool send_data(List<Item> &items);
|
||||||
bool initialize_tables (JOIN *join);
|
bool initialize_tables (JOIN *join);
|
||||||
void send_error(uint errcode,const char *err);
|
void send_error(uint errcode,const char *err);
|
||||||
@ -1455,7 +1468,7 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class multi_update :public select_result
|
class multi_update :public select_result_interceptor
|
||||||
{
|
{
|
||||||
TABLE_LIST *all_tables, *update_tables, *table_being_updated;
|
TABLE_LIST *all_tables, *update_tables, *table_being_updated;
|
||||||
THD *thd;
|
THD *thd;
|
||||||
@ -1474,7 +1487,6 @@ public:
|
|||||||
List<Item> *values, enum_duplicates handle_duplicates);
|
List<Item> *values, enum_duplicates handle_duplicates);
|
||||||
~multi_update();
|
~multi_update();
|
||||||
int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
|
int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
|
||||||
bool send_fields(List<Item> &list, uint flag) { return 0; }
|
|
||||||
bool send_data(List<Item> &items);
|
bool send_data(List<Item> &items);
|
||||||
bool initialize_tables (JOIN *join);
|
bool initialize_tables (JOIN *join);
|
||||||
void send_error(uint errcode,const char *err);
|
void send_error(uint errcode,const char *err);
|
||||||
@ -1483,7 +1495,7 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class select_dumpvar :public select_result {
|
class select_dumpvar :public select_result_interceptor {
|
||||||
ha_rows row_count;
|
ha_rows row_count;
|
||||||
public:
|
public:
|
||||||
List<LEX_STRING> var_list;
|
List<LEX_STRING> var_list;
|
||||||
@ -1491,7 +1503,6 @@ public:
|
|||||||
select_dumpvar(void) { var_list.empty(); vars.empty(); row_count=0;}
|
select_dumpvar(void) { var_list.empty(); vars.empty(); row_count=0;}
|
||||||
~select_dumpvar() {}
|
~select_dumpvar() {}
|
||||||
int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
|
int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
|
||||||
bool send_fields(List<Item> &list, uint flag) {return 0;}
|
|
||||||
bool send_data(List<Item> &items);
|
bool send_data(List<Item> &items);
|
||||||
bool send_eof();
|
bool send_eof();
|
||||||
void cleanup();
|
void cleanup();
|
||||||
|
@ -1065,6 +1065,12 @@ static int mysql_test_select(Prepared_statement *stmt,
|
|||||||
DBUG_RETURN(1);
|
DBUG_RETURN(1);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if (!lex->result && !(lex->result= new (&stmt->mem_root) select_send))
|
||||||
|
{
|
||||||
|
send_error(thd);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
if (open_and_lock_tables(thd, tables))
|
if (open_and_lock_tables(thd, tables))
|
||||||
{
|
{
|
||||||
send_error(thd);
|
send_error(thd);
|
||||||
@ -1088,8 +1094,13 @@ static int mysql_test_select(Prepared_statement *stmt,
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (send_prep_stmt(stmt, lex->select_lex.item_list.elements) ||
|
List<Item> &fields= lex->select_lex.item_list;
|
||||||
thd->protocol_simple.send_fields(&lex->select_lex.item_list, 0)
|
/*
|
||||||
|
We can use lex->result as it should've been
|
||||||
|
prepared in unit->prepare call above.
|
||||||
|
*/
|
||||||
|
if (send_prep_stmt(stmt, lex->result->field_count(fields)) ||
|
||||||
|
lex->result->send_fields(fields, 0)
|
||||||
#ifndef EMBEDDED_LIBRARY
|
#ifndef EMBEDDED_LIBRARY
|
||||||
|| net_flush(&thd->net)
|
|| net_flush(&thd->net)
|
||||||
#endif
|
#endif
|
||||||
|
@ -10541,6 +10541,22 @@ static void test_bug5315()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void test_bug6059()
|
||||||
|
{
|
||||||
|
MYSQL_STMT *stmt;
|
||||||
|
const char *stmt_text;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
myheader("test_bug6059");
|
||||||
|
|
||||||
|
stmt_text= "SELECT 'foo' INTO OUTFILE 'x.3'";
|
||||||
|
|
||||||
|
stmt= mysql_stmt_init(mysql);
|
||||||
|
rc= mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text));
|
||||||
|
DIE_UNLESS(mysql_stmt_field_count(stmt) == 0);
|
||||||
|
mysql_stmt_close(stmt);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Read and parse arguments and MySQL options from my.cnf
|
Read and parse arguments and MySQL options from my.cnf
|
||||||
*/
|
*/
|
||||||
@ -10851,6 +10867,7 @@ int main(int argc, char **argv)
|
|||||||
test_bug5194(); /* bulk inserts in prepared mode */
|
test_bug5194(); /* bulk inserts in prepared mode */
|
||||||
test_bug5315(); /* check that mysql_change_user closes all
|
test_bug5315(); /* check that mysql_change_user closes all
|
||||||
prepared statements */
|
prepared statements */
|
||||||
|
test_bug6059(); /* correct metadata for SELECT ... INTO OUTFILE */
|
||||||
/*
|
/*
|
||||||
XXX: PLEASE RUN THIS PROGRAM UNDER VALGRIND AND VERIFY THAT YOUR TEST
|
XXX: PLEASE RUN THIS PROGRAM UNDER VALGRIND AND VERIFY THAT YOUR TEST
|
||||||
DOESN'T CONTAIN WARNINGS/ERRORS BEFORE YOU PUSH.
|
DOESN'T CONTAIN WARNINGS/ERRORS BEFORE YOU PUSH.
|
||||||
|
Reference in New Issue
Block a user