3796 Praveenkumar Hulakund 2012-01-27
BUG#11764786: 57657: TEMPORARY MERGE TABLE WITH TEMPORARY UNDERLYING IS BROKEN BY ALTER
Analysis:
--------------------------------------------
When merge table is created, then MyISAM MERGE creates a file .MRG. Engine adds
names of the tables (children tables) and insert method in this file. So, if we
consider the tables listed in "HOW TO REPEAT" section then content of .MRG file
of "tm_temp_temp"table will be as below
>cat #<tmp_file_name>.MRG
-----------------------
./test/t1_temp
#INSERT_METHOD=FIRST
-----------------------
So for both, temporary and non-temporary children table "table name" is added in
.MRG file in "./<db_name>/<table_name>" format. So that the table can be opened
using <db_name>.<table_name> [from comments written in function
"ha_myisammrg::create"].
When sql query to alter merge table "tm_temp_temp" is called then "mysql_alter_table"
function calls "open_and_lock_tables" which in turn call the table open function of
MyISAM MERGE storage. MyISAM MERGE storage handler, holds all the open table
information in open_tables of type MYRG_TABLE.So, in open function of MyISAM Merge
handler, table "tm_temp_temp" is opened and held with the open_tables.
Since, this merge table contains child_table (t1_temp). All the child table are
opened and TABLE_LIST of child table will be "children_l" member of MyISAM Merge handler.
children_l is doubly linked list of all child tables.
children_l -> TABLE_LIST::next_global -> TABLE_LIST::next_global
# # ^ # ^
# # | # |
# # +--------- TABLE_LIST::prev_global
# # |
# |<--- TABLE_LIST::prev_global |
# |
children_last_l -----------------------------------------+
And information of all child tables are also held in the open_tables of MyISAM merge
handler.
The name of the table file for each table opened is held in the "filename" of
open_tables->table (type is struct st_myisam_info).
* Why merge table is getting corrupted in "alter table"?
Alter table calls function "update_create_info" function. In "update_create_info"
function of MyISAM Merge Handler, merge list (TABLE_LIST of child tables) is
populated in create_info object of type HA_CREATE_INFO. And this list is then used
to create new .MRG file to reflect the alter operations.
In function, "update_create_info" of MyISAM MERGE, the DB name and the TABLE name
are extracted from the filename stored in the member "open_tables->table->filename"
as below,
split_file_name(open_table->table->filename, &db_name, &file_name);
This db name and file name are stored in the create_info object. And while creating
the .MRG file, table name of the child table is written into it in the
"./<db_name>/<table_name> format.
When merge table has non-temporary child tables then "filename" contains proper db
name and file name.
But when merge table has temporary tables then "filename" has a temporary filename
created in "/tmp" directory (for eg: /tmp/#sql7a3b_2_1). So when "split_file_name"
function is called with this value, "tmp" is stored in the db name "#sql7a3b_2_1"
is stored in the file name. So here while creating new .MRG file, "./tmp/#sql7a3b_2_1"
written as a child entry.
>cat #<tmp_file_name>.MRG
-----------------------
"./tmp/#sql7a3b_2_1"
#INSERT_METHOD=LAST
-----------------------
After this, any operation on merge table gives error as its fails to find "tmp" DB
and table name "#sql7a3b_2_1". And because of the same reason "check table" also
reported the "table corruption" error.
*. Why we wont see this issue while creating merge table?
While creating merge table, "create_info" object of type HA_CREATE_INFO is formed by
parser with valid "merge_list" (with proper DB name and table name for temporary
and non-temporary tables). So, create table function of MyISAM MERGE creates .MRG file.
So, .MRG contains proper entry for table after creating a table.
>cat #<tmp_file_name>.MRG
-----------------------
./test/t1_temp
#INSERT_METHOD=FIRST
-----------------------
Since, create_info populated with merge_list by parser itself, function "update_create_info"
is not called.
Possible Fix:
--------------------------------------------
Instead of getting db name and table name from "open_tables->table->filename" in
update_create_info function, the db name and table name can be obtained from
the TABLE_LIST of each child table stored in the "children_l" of MyISAM MERGE
handler. For both the temporary and non-temporary child table the TABLE_LIST
is propogated with proper DB name and table name in members "db" and "table_name".
So as a, logic of the "update_create_info" MyISAM MERGE handler is
changed so that it gets db name and table from TABLE_LIST stored in "children_l".
modified:
mysql-test/r/merge.result
mysql-test/t/merge.test
storage/myisammrg/ha_myisammrg.cc
3795 Dmitry Shulga 2012-01-27
Patch for bug#12872824 (formerly known as 62125): testing stored function
result for null incorrectly yields 1292 warning.
The reason for this bug was that when server executed expression 'IS NULL'
for return value of some stored function it calculated the return
value before checking it for NULL. During return value calculation
it's assumed that a value has INTEGER type that is not true in general.
The solution is to evaluate return value for NULL without its calculation.
@ mysql-test/r/sp.result
Added results of test for bug#12872824.
@ mysql-test/t/sp.test
Added test for bug#12872824.
@ sql/item_func.cc
Added implementation for virtual method update_null_value into
Item_func_sp. Implementation for this method doesn't call
Item::update_null_value() in order to get null flag for
return value. Instead we call private method execute() that
check return value for null and set null flag.
modified:
mysql-test/r/sp.result
mysql-test/t/sp.test
sql/item_func.cc
sql/item_func.h
=== modified file 'mysql-test/r/merge.result'
--- a/mysql-test/r/merge.result revid:dmitry.shulga@stripped
+++ b/mysql-test/r/merge.result revid:praveenkumar.hulakund@stripped
@@ -3801,3 +3801,23 @@ ERROR HY000: Unable to open underlying t
DROP TRIGGER trg1;
DROP TABLE t1;
DROP TABLE m1;
+#
+# Test for bug #11764786 - 57657: TEMPORARY MERGE TABLE WITH TEMPORARY
+# UNDERLYING TABLE, IS BROKEN BY ALTER.
+#
+DROP TABLES IF EXISTS t1, t2, t3, t4;
+CREATE TEMPORARY TABLE t1(i INT) ENGINE= MyISAM;
+CREATE TEMPORARY TABLE t2(i INT) ENGINE= MERGE UNION= (t1) INSERT_METHOD= LAST;
+ALTER TABLE t2 INSERT_METHOD= FIRST;
+CHECK TABLE t2;
+Table Op Msg_type Msg_text
+test.t2 check status OK
+CREATE TABLE t3(i INT) ENGINE= MyISAM;
+CREATE TABLE t4(i int) ENGINE= MERGE UNION= (t3) INSERT_METHOD= LAST;
+ALTER TABLE t4 INSERT_METHOD= FIRST;
+CHECK TABLE t4;
+Table Op Msg_type Msg_text
+test.t4 check status OK
+# Clean-up
+DROP TABLES t1, t2, t3, t4;
+# End of bug #11764786 - 57657
=== modified file 'mysql-test/t/merge.test'
--- a/mysql-test/t/merge.test revid:dmitry.shulga@stripped
+++ b/mysql-test/t/merge.test revid:praveenkumar.hulakund@stripped
@@ -2905,6 +2905,25 @@ DROP TRIGGER trg1;
DROP TABLE t1;
DROP TABLE m1;
+--echo #
+--echo # Test for bug #11764786 - 57657: TEMPORARY MERGE TABLE WITH TEMPORARY
+--echo # UNDERLYING TABLE, IS BROKEN BY ALTER.
+--echo #
+--disable_warnings
+DROP TABLES IF EXISTS t1, t2, t3, t4;
+--enable_warnings
+CREATE TEMPORARY TABLE t1(i INT) ENGINE= MyISAM;
+CREATE TEMPORARY TABLE t2(i INT) ENGINE= MERGE UNION= (t1) INSERT_METHOD= LAST;
+ALTER TABLE t2 INSERT_METHOD= FIRST;
+CHECK TABLE t2;
+
+CREATE TABLE t3(i INT) ENGINE= MyISAM;
+CREATE TABLE t4(i int) ENGINE= MERGE UNION= (t3) INSERT_METHOD= LAST;
+ALTER TABLE t4 INSERT_METHOD= FIRST;
+CHECK TABLE t4;
+--echo # Clean-up
+DROP TABLES t1, t2, t3, t4;
+--echo # End of bug #11764786 - 57657
--disable_result_log
--disable_query_log
=== modified file 'storage/myisammrg/ha_myisammrg.cc'
--- a/storage/myisammrg/ha_myisammrg.cc revid:dmitry.shulga@stripped
+++ b/storage/myisammrg/ha_myisammrg.cc revid:praveenkumar.hulakund@stripped
@@ -1469,31 +1469,36 @@ void ha_myisammrg::update_create_info(HA
if (!(create_info->used_fields & HA_CREATE_USED_UNION))
{
- MYRG_TABLE *open_table;
+ TABLE_LIST *child_table;
THD *thd=current_thd;
create_info->merge_list.next= &create_info->merge_list.first;
create_info->merge_list.elements=0;
- for (open_table=file->open_tables ;
- open_table != file->end_table ;
- open_table++)
+ if (children_l != NULL)
{
- TABLE_LIST *ptr;
- LEX_STRING db, name;
- LINT_INIT(db.str);
-
- if (!(ptr = (TABLE_LIST *) thd->calloc(sizeof(TABLE_LIST))))
- goto err;
- split_file_name(open_table->table->filename, &db, &name);
- if (!(ptr->table_name= thd->strmake(name.str, name.length)))
- goto err;
- if (db.length && !(ptr->db= thd->strmake(db.str, db.length)))
- goto err;
-
- create_info->merge_list.elements++;
- (*create_info->merge_list.next) = ptr;
- create_info->merge_list.next= &ptr->next_local;
+ for (child_table= children_l;;
+ child_table= child_table->next_global)
+ {
+ TABLE_LIST *ptr;
+
+ if (!(ptr= (TABLE_LIST *) thd->calloc(sizeof(TABLE_LIST))))
+ goto err;
+
+ if (!(ptr->table_name= thd->strmake(child_table->table_name,
+ child_table->table_name_length)))
+ goto err;
+ if (child_table->db && !(ptr->db= thd->strmake(child_table->db,
+ child_table->db_length)))
+ goto err;
+
+ create_info->merge_list.elements++;
+ (*create_info->merge_list.next)= ptr;
+ create_info->merge_list.next= &ptr->next_local;
+
+ if (&child_table->next_global == children_last_l)
+ break;
+ }
}
*create_info->merge_list.next=0;
}
No bundle (reason: useless for push emails).
| Thread |
|---|
| • bzr push into mysql-trunk branch (praveenkumar.hulakund:3795 to 3796)Bug#11764786 | Praveenkumar Hulakund | 30 Jan |