From: Date: February 14 2008 8:42pm Subject: bk commit into 5.0 tree (davi:1.2578) BUG#32265 List-Archive: http://lists.mysql.com/commits/42303 X-Bug: 32265 Message-Id: <20080214194251.65295596B5F@endora.local> Below is the list of changes that have just been committed into a local 5.0 repository of davi. When davi does a push these changes will be propagated to the main repository and, within 24 hours after the push, to the public repository. For information on how to access the public repository see http://dev.mysql.com/doc/mysql/en/installing-source-tree.html ChangeSet@stripped, 2008-02-14 17:42:46-02:00, davi@stripped +2 -0 Bug#32265 Server returns different metadata if prepared statement is used Executing a prepared statement associated with a materialized cursor yields to the client a metadata packet with wrong table and database names. The problem was occurring because the server was sending the the name of the temporary table used by the cursor instead of the table name of the original table. The same problem occurs when selecting from views, in which case the table name was being sent and not the name of the view. The solution is to backup the table and database names of the fields of the original tables or views and restore then later into the fields of the temporary table. sql/sql_cursor.cc@stripped, 2008-02-14 17:42:44-02:00, davi@stripped +108 -5 Backup the field's names and restore it when the metadata needs to be sent to the client. This is done this way to avoid the convoluted issue of creating the item list in the send_fields method. tests/mysql_client_test.c@stripped, 2008-02-14 17:42:44-02:00, davi@stripped +82 -0 Add test case for Bug#32265 diff -Nrup a/sql/sql_cursor.cc b/sql/sql_cursor.cc --- a/sql/sql_cursor.cc 2006-12-30 18:02:07 -02:00 +++ b/sql/sql_cursor.cc 2008-02-14 17:42:44 -02:00 @@ -70,6 +70,17 @@ public: /* + Table and database name of a item before the temp table is created. +*/ + +typedef struct +{ + const char *db_name; + const char *table_name; +} Field_tuple; + + +/* Materialized_cursor -- an insensitive materialized server-side cursor. The result set of this cursor is saved in a temporary table at open. The cursor itself is simply an interface for the @@ -83,10 +94,14 @@ class Materialized_cursor: public Server SELECT_LEX_UNIT fake_unit; TABLE *table; List item_list; + List *name_list; ulong fetch_limit; ulong fetch_count; +private: + void restore_fields_names(void); public: - Materialized_cursor(select_result *result, TABLE *table); + Materialized_cursor(select_result *result, TABLE *table, + List *name_list); virtual bool is_open() const { return table != 0; } virtual int open(JOIN *join __attribute__((unused))); @@ -108,9 +123,14 @@ public: class Select_materialize: public select_union { select_result *result; /* the result object of the caller (PS or SP) */ + List *name_list; +private: + bool backup_fields_names(List &item_list); public: - Select_materialize(select_result *result_arg) :result(result_arg) {} + Select_materialize(select_result *result_arg) + : result(result_arg), name_list(NULL) {} virtual bool send_fields(List &list, uint flags); + List *get_fields_names(void) { return name_list; } }; @@ -207,9 +227,12 @@ int mysql_open_cursor(THD *thd, uint fla Materialized_cursor *materialized_cursor; TABLE *table= result_materialize->table; MEM_ROOT *mem_root= &table->mem_root; + List *name_list= result_materialize->get_fields_names(); + + DBUG_ASSERT(name_list != NULL); if (!(materialized_cursor= new (mem_root) - Materialized_cursor(result, table))) + Materialized_cursor(result, table, name_list))) { rc= 1; goto err_open; @@ -533,9 +556,11 @@ Sensitive_cursor::~Sensitive_cursor() ****************************************************************************/ Materialized_cursor::Materialized_cursor(select_result *result_arg, - TABLE *table_arg) + TABLE *table_arg, + List *list_arg) :Server_side_cursor(&table_arg->mem_root, result_arg), table(table_arg), + name_list(list_arg), fetch_limit(0), fetch_count(0) { @@ -544,6 +569,41 @@ Materialized_cursor::Materialized_cursor } +static inline Item_ident *get_ident_item(Item *item) +{ + Item::Type type= item->type(); + + if (type == Item::FIELD_ITEM || type == Item::REF_ITEM) + return (Item_ident *) item; + + type= item->real_item()->type(); + + if (type == Item::FIELD_ITEM || type == Item::REF_ITEM) + return (Item_field *) item->real_item(); + + return NULL; +} + + +void Materialized_cursor::restore_fields_names(void) +{ + Item *item; + Item_ident *ident; + Field_tuple *tuple; + List_iterator_fast it(item_list); + List_iterator_fast name_it(*name_list); + + while ((item= it++, tuple= name_it++)) + { + ident= get_ident_item(item); + if (!ident) + continue; + ident->db_name= tuple->db_name; + ident->table_name= tuple->table_name; + } +} + + int Materialized_cursor::open(JOIN *join __attribute__((unused))) { THD *thd= fake_unit.thd; @@ -559,6 +619,13 @@ int Materialized_cursor::open(JOIN *join if (rc == 0) { /* + Metadata must not contain table and database names of the + materialized table (item's table and database name is "" + at this point.) + */ + restore_fields_names(); + + /* Now send the result set metadata to the client. We need to do it here, as in Select_materialize::send_fields the items for column types are not yet created (send_fields requires @@ -658,12 +725,48 @@ Materialized_cursor::~Materialized_curso Select_materialize ****************************************************************************/ + +bool Select_materialize::backup_fields_names(List &item_list) +{ + Item *item; + Item_ident *ident; + Field_tuple *tuple; + List_iterator_fast it(item_list); + MEM_ROOT *mem_root= &table->mem_root; + + if (!(name_list= new (mem_root) List)) + return TRUE; + + while ((item= it++)) + { + if (!(tuple= (Field_tuple *) alloc_root(mem_root, sizeof(Field_tuple)))) + return FALSE; + + tuple->db_name= tuple->table_name= NULL; + + name_list->push_back(tuple, mem_root); + + if (!(ident= get_ident_item(item))) + continue; + + if (ident->db_name) + tuple->db_name= strdup_root(mem_root, ident->db_name); + + if (ident->table_name) + tuple->table_name= strdup_root(mem_root, ident->table_name); + } + + return FALSE; +} + + bool Select_materialize::send_fields(List &list, uint flags) { DBUG_ASSERT(table == 0); if (create_result_table(unit->thd, unit->get_unit_column_types(), FALSE, thd->options | TMP_TABLE_ALL_COLUMNS, "")) return TRUE; - return FALSE; + + return backup_fields_names(list); } diff -Nrup a/tests/mysql_client_test.c b/tests/mysql_client_test.c --- a/tests/mysql_client_test.c 2007-12-01 07:12:28 -02:00 +++ b/tests/mysql_client_test.c 2008-02-14 17:42:44 -02:00 @@ -16152,6 +16152,87 @@ static void test_bug31669() DBUG_VOID_RETURN; } + +/** + Bug#32265 Server returns different metadata if prepared statement is used +*/ + +static void test_bug32265() +{ + int rc, i; + MYSQL_STMT *stmt; + MYSQL_FIELD *field; + + DBUG_ENTER("test_bug32265"); + myheader("test_bug32265"); + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1"); + myquery(rc); + rc= mysql_query(mysql, "CREATE TABLE t1 (a INTEGER)"); + myquery(rc); + rc= mysql_query(mysql, "INSERT INTO t1 VALUES (1)"); + myquery(rc); + rc= mysql_query(mysql, "CREATE VIEW v1 AS SELECT * FROM t1"); + myquery(rc); + + stmt= open_cursor("SELECT * FROM t1"); + rc= mysql_stmt_execute(stmt); + check_execute(stmt, rc); + + field= stmt->mysql->fields; + DIE_UNLESS(strcmp(field->table, "t1") == 0); + DIE_UNLESS(strcmp(field->org_table, "t1") == 0); + DIE_UNLESS(strcmp(field->db, "client_test_db") == 0); + mysql_stmt_close(stmt); + + stmt= open_cursor("SELECT a '' FROM t1 ``"); + rc= mysql_stmt_execute(stmt); + check_execute(stmt, rc); + + field= stmt->mysql->fields; + DIE_UNLESS(strcmp(field->table, "") == 0); + DIE_UNLESS(strcmp(field->org_table, "t1") == 0); + DIE_UNLESS(strcmp(field->db, "client_test_db") == 0); + mysql_stmt_close(stmt); + + stmt= open_cursor("SELECT a '' FROM t1 ``"); + rc= mysql_stmt_execute(stmt); + check_execute(stmt, rc); + + field= stmt->mysql->fields; + DIE_UNLESS(strcmp(field->table, "") == 0); + DIE_UNLESS(strcmp(field->org_table, "t1") == 0); + DIE_UNLESS(strcmp(field->db, "client_test_db") == 0); + mysql_stmt_close(stmt); + + stmt= open_cursor("SELECT * FROM v1"); + rc= mysql_stmt_execute(stmt); + check_execute(stmt, rc); + + field= stmt->mysql->fields; + DIE_UNLESS(strcmp(field->table, "v1") == 0); + DIE_UNLESS(strcmp(field->org_table, "t1") == 0); + DIE_UNLESS(strcmp(field->db, "client_test_db") == 0); + mysql_stmt_close(stmt); + + stmt= open_cursor("SELECT * FROM v1 /* SIC */ GROUP BY 1"); + rc= mysql_stmt_execute(stmt); + check_execute(stmt, rc); + + field= stmt->mysql->fields; + DIE_UNLESS(strcmp(field->table, "v1") == 0); + DIE_UNLESS(strcmp(field->org_table, "t1") == 0); + DIE_UNLESS(strcmp(field->db, "client_test_db") == 0); + mysql_stmt_close(stmt); + + rc= mysql_query(mysql, "DROP VIEW v1"); + myquery(rc); + rc= mysql_query(mysql, "DROP TABLE t1"); + myquery(rc); + + DBUG_VOID_RETURN; +} + /* Read and parse arguments and MySQL options from my.cnf */ @@ -16446,6 +16527,7 @@ static struct my_tests_st my_tests[]= { { "test_bug29948", test_bug29948 }, { "test_bug29306", test_bug29306 }, { "test_bug31669", test_bug31669 }, + { "test_bug32265", test_bug32265 }, { 0, 0 } };