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> item_list;
+ List<Field_tuple> *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<Field_tuple> *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<Field_tuple> *name_list;
+private:
+ bool backup_fields_names(List<Item> &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<Item> &list, uint flags);
+ List<Field_tuple> *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<Field_tuple> *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<Field_tuple> *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<Item> it(item_list);
+ List_iterator_fast<Field_tuple> 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> &item_list)
+{
+ Item *item;
+ Item_ident *ident;
+ Field_tuple *tuple;
+ List_iterator_fast<Item> it(item_list);
+ MEM_ROOT *mem_root= &table->mem_root;
+
+ if (!(name_list= new (mem_root) List<Field_tuple>))
+ 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<Item> &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 }
};