diff options
author | konstantin@mysql.com <> | 2004-10-21 18:33:53 +0400 |
---|---|---|
committer | konstantin@mysql.com <> | 2004-10-21 18:33:53 +0400 |
commit | 49a58fc64ee811fd71a974f493540c19d98ada73 (patch) | |
tree | 7058ab30d1d358a25767f8022ee18348b8f22709 | |
parent | 37ab172624222a9c414cb265e4c2eff819fd17f5 (diff) | |
download | mariadb-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.h | 45 | ||||
-rw-r--r-- | sql/sql_prepare.cc | 15 | ||||
-rw-r--r-- | tests/client_test.c | 17 |
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. |