summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorkonstantin@mysql.com <>2004-10-21 18:33:53 +0400
committerkonstantin@mysql.com <>2004-10-21 18:33:53 +0400
commit49a58fc64ee811fd71a974f493540c19d98ada73 (patch)
tree7058ab30d1d358a25767f8022ee18348b8f22709
parent37ab172624222a9c414cb265e4c2eff819fd17f5 (diff)
downloadmariadb-git-49a58fc64ee811fd71a974f493540c19d98ada73.tar.gz
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).
-rw-r--r--sql/sql_class.h45
-rw-r--r--sql/sql_prepare.cc15
-rw-r--r--tests/client_test.c17
3 files changed, 58 insertions, 19 deletions
diff --git a/sql/sql_class.h b/sql/sql_class.h
index aea31f7db54..58514abb7b0 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -1109,6 +1109,13 @@ public:
unit= u;
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_data(List<Item> &items)=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 {
public:
select_send() {}
@@ -1132,7 +1153,7 @@ public:
};
-class select_to_file :public select_result {
+class select_to_file :public select_result_interceptor {
protected:
sql_exchange *exchange;
File file;
@@ -1144,7 +1165,6 @@ public:
select_to_file(sql_exchange *ex) :exchange(ex), file(-1),row_count(0L)
{ path[0]=0; }
~select_to_file();
- bool send_fields(List<Item> &list, uint flag) { return 0; }
void send_error(uint errcode,const char *err);
bool send_eof();
void cleanup();
@@ -1171,7 +1191,7 @@ public:
};
-class select_insert :public select_result {
+class select_insert :public select_result_interceptor {
public:
TABLE *table;
List<Item> *fields;
@@ -1187,8 +1207,6 @@ class select_insert :public select_result {
}
~select_insert();
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);
void send_error(uint errcode,const char *err);
bool send_eof();
@@ -1271,7 +1289,7 @@ public:
}
};
-class select_union :public select_result {
+class select_union :public select_result_interceptor {
public:
TABLE *table;
COPY_INFO info;
@@ -1280,8 +1298,6 @@ class select_union :public select_result {
select_union(TABLE *table_par);
~select_union();
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_eof();
bool flush();
@@ -1289,13 +1305,12 @@ class select_union :public select_result {
};
/* Base subselect interface class */
-class select_subselect :public select_result
+class select_subselect :public select_result_interceptor
{
protected:
Item_subselect *item;
public:
select_subselect(Item_subselect *item);
- bool send_fields(List<Item> &list, uint flag) { return 0; };
bool send_data(List<Item> &items)=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;
Unique **tempfiles;
@@ -1445,8 +1460,6 @@ public:
multi_delete(THD *thd, TABLE_LIST *dt, uint num_of_tables);
~multi_delete();
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 initialize_tables (JOIN *join);
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;
THD *thd;
@@ -1474,7 +1487,6 @@ public:
List<Item> *values, enum_duplicates handle_duplicates);
~multi_update();
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 initialize_tables (JOIN *join);
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;
public:
List<LEX_STRING> var_list;
@@ -1491,7 +1503,6 @@ public:
select_dumpvar(void) { var_list.empty(); vars.empty(); row_count=0;}
~select_dumpvar() {}
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_eof();
void cleanup();
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index 27d98fdfeba..545bc7bcc21 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -1065,6 +1065,12 @@ static int mysql_test_select(Prepared_statement *stmt,
DBUG_RETURN(1);
#endif
+ if (!lex->result && !(lex->result= new (&stmt->mem_root) select_send))
+ {
+ send_error(thd);
+ goto err;
+ }
+
if (open_and_lock_tables(thd, tables))
{
send_error(thd);
@@ -1088,8 +1094,13 @@ static int mysql_test_select(Prepared_statement *stmt,
}
else
{
- if (send_prep_stmt(stmt, lex->select_lex.item_list.elements) ||
- thd->protocol_simple.send_fields(&lex->select_lex.item_list, 0)
+ List<Item> &fields= lex->select_lex.item_list;
+ /*
+ 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
|| net_flush(&thd->net)
#endif
diff --git a/tests/client_test.c b/tests/client_test.c
index 0b30cc3386d..1d2a85e54f4 100644
--- a/tests/client_test.c
+++ b/tests/client_test.c
@@ -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
*/
@@ -10851,6 +10867,7 @@ int main(int argc, char **argv)
test_bug5194(); /* bulk inserts in prepared mode */
test_bug5315(); /* check that mysql_change_user closes all
prepared statements */
+ test_bug6059(); /* correct metadata for SELECT ... INTO OUTFILE */
/*
XXX: PLEASE RUN THIS PROGRAM UNDER VALGRIND AND VERIFY THAT YOUR TEST
DOESN'T CONTAIN WARNINGS/ERRORS BEFORE YOU PUSH.