From 184ecd9899d215fece148f577bc7ba06a9ef77cd Mon Sep 17 00:00:00 2001 From: Dmitry Shulga Date: Thu, 9 Jun 2011 23:30:52 +0700 Subject: Fixed bug#11840395 (formerly known as bug#60347: THE STRING "VERSIONDATA" SEEMS TO BE 'LEAKING' INTO THE SCHEMA NAME SPACE) and bug#12428824 (Parser stack overflow and crash in sp_add_used_routine with obscure query). The first problem was that attempts to call a stored function by its fully qualified name ended up with unwarranted error "ERROR 1305 (42000): FUNCTION someMixedCaseDb.my_function_name does not exist" if this function belonged to a schema that had uppercase letters in its name AND --lower_case_table_names was equal to either 1 or 2. The second problem was that 5.5 version of MySQL server might have crashed when a user tried to call stored function with too long name or too long database name (i.e if a function and database name combined occupied more than 2*3*64 bytes in utf8). This issue didn't affect versions of server < 5.5. The first problem was caused by the fact that in cases when a stored function was called by its fully qualified name we didn't lowercase name of its schema before performing look up of the function in mysql.proc table even although lower_case_table_names mode was on. As result we were unable to find this function since during its creation we store lowercased version of schema name in the system table in this mode and field for schema name uses binary collation. Calls to stored functions were unaffected by this problem since for them schema name is converted to lowercase as necessary. The reason for the second bug was that MySQL Server didn't check length of function name and database name before proceeding with execution of stored function. As a consequence too long database name or function name caused buffer overruns in places where the code assumes that their length is within fixed limits, like mdl_key_init() in 5.5. Again this issue didn't affect calls to stored procedures as for them length of schema name and procedure name are properly checked. This patch fixes both these bugs by adding calls to check_db_name() and check_routine_name() to grammar rule which corresponds to a call to a stored function. These functions ensure that length of database name and function name for routine called is within standard limit. Moreover call to check_db_name() handles conversion of database name to lowercase if --lower_case_table_names mode is on. Note that even although the second issue seems to be only reproducible in 5.5 we still add code fixing it to 5.1 to be on the safe side (and make code a bit more robust against possible future changes). mysql-test/r/sp-error.result: Added testcase results for bug#12428824. mysql-test/r/sp.result: Added testcase result for bug#11840395. mysql-test/t/sp-error.test: Added testcase for bug#12428824. mysql-test/t/sp.test: Added testcase for bug#11840395. sql/sql_yacc.yy: Modified 'function_call_generic' rule to call check_db_name() and check_routine_name() in order to ensure that lengths of database name and function name are within limits. check_db_name() is also responsible for normalizing function's database name for lookup in cases when lowercase_table_names mode is on. --- sql/sql_yacc.yy | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'sql') diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 4e24e69af42..719426015bd 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -8020,6 +8020,11 @@ function_call_generic: Create_func *builder; Item *item= NULL; + if (check_routine_name(&$1)) + { + MYSQL_YYABORT; + } + /* Implementation note: names are resolved with the following order: @@ -8083,6 +8088,16 @@ function_call_generic: version() (a vendor can specify any schema). */ + if (!$1.str || check_db_name(&$1)) + { + my_error(ER_WRONG_DB_NAME, MYF(0), $1.str); + MYSQL_YYABORT; + } + if (check_routine_name(&$3)) + { + MYSQL_YYABORT; + } + builder= find_qualified_function_builder(thd); DBUG_ASSERT(builder); item= builder->create(thd, $1, $3, true, $5); -- cgit v1.2.1 From 08ecbd5adb9051b77dca4cea0988ea286366a409 Mon Sep 17 00:00:00 2001 From: Tor Didriksen Date: Mon, 11 Jul 2011 11:20:19 +0200 Subject: Bug#11765255 - 58201: VALGRIND/CRASH WHEN ORDERING BY MULTIPLE AGGREGATE FUNCTIONS We must allocate a larger ref_pointer_array. We failed to account for extra items allocated here: #0 find_order_in_list uint el= all_fields.elements; all_fields.push_front(order_item); /* Add new field to field list. */ ref_pointer_array[el]= order_item; order->item= ref_pointer_array + el; #1 setup_order #2 setup_without_group #3 JOIN::prepare mysql-test/r/order_by.result: New test case. mysql-test/r/union.result: New test case. mysql-test/t/order_by.test: New test case. mysql-test/t/union.test: New test case. sql/sql_lex.cc: find_order_in_list() may need some extra space, so multiply og_num by two. sql/sql_union.cc: For UNION, the 'n_sum_items' are accumulated in the "global_parameters" select_lex. This number must be propagated to setup_ref_array() When preparing a 'fake_select_lex' we need to use global_parameters->order_list rather than fake_select_lex->order_list (see comments inside st_select_lex_unit::cleanup) --- sql/sql_lex.cc | 3 +++ sql/sql_union.cc | 39 ++++++++++++++++++++++++++++++--------- 2 files changed, 33 insertions(+), 9 deletions(-) (limited to 'sql') diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index f0289ab86ce..a7b23746155 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -1987,6 +1987,9 @@ bool st_select_lex::setup_ref_array(THD *thd, uint order_group_num) if (ref_pointer_array) return 0; + // find_order_in_list() may need some extra space, so multiply by two. + order_group_num*= 2; + /* We have to create array in prepared statement memory if it is prepared statement diff --git a/sql/sql_union.cc b/sql/sql_union.cc index a70de945492..2be47d95a26 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -1,5 +1,4 @@ -/* - Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -403,15 +402,27 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, fake_select_lex->table_list.empty(); DBUG_RETURN(TRUE); } + + /* + Fake st_select_lex should have item list for correct ref_array + allocation. + */ fake_select_lex->item_list= item_list; thd_arg->lex->current_select= fake_select_lex; + + /* + We need to add up n_sum_items in order to make the correct + allocation in setup_ref_array(). + */ + fake_select_lex->n_child_sum_items+= global_parameters->n_sum_items; + saved_error= fake_select_lex->join-> prepare(&fake_select_lex->ref_pointer_array, fake_select_lex->table_list.first, 0, 0, - fake_select_lex->order_list.elements, - fake_select_lex->order_list.first, + global_parameters->order_list.elements, // og_num + global_parameters->order_list.first, // order NULL, NULL, NULL, fake_select_lex, this); fake_select_lex->table_list.empty(); @@ -579,11 +590,21 @@ bool st_select_lex_unit::exec() } fake_select_lex->join->no_const_tables= TRUE; - /* - Fake st_select_lex should have item list for correctref_array - allocation. - */ - fake_select_lex->item_list= item_list; + /* + Fake st_select_lex should have item list for correct ref_array + allocation. + */ + fake_select_lex->item_list= item_list; + + /* + We need to add up n_sum_items in order to make the correct + allocation in setup_ref_array(). + Don't add more sum_items if we have already done JOIN::prepare + for this (with a different join object) + */ + if (!fake_select_lex->ref_pointer_array) + fake_select_lex->n_child_sum_items+= global_parameters->n_sum_items; + saved_error= mysql_select(thd, &fake_select_lex->ref_pointer_array, &result_table_list, 0, item_list, NULL, -- cgit v1.2.1 From cfcd49b467dc5de004310db1ff0810842ea3bb56 Mon Sep 17 00:00:00 2001 From: Tor Didriksen Date: Fri, 15 Jul 2011 14:07:38 +0200 Subject: Bug#12406055 BUFFER OVERFLOW OF VARIABLE 'BUFF' IN STRING::SET_REAL The buffer was simply too small. In 5.5 and trunk, the size is 311 + 31, in 5.1 and below, the size is 331 client/sql_string.cc: Increase buffer size in String::set(double, ...) include/m_string.h: Increase FLOATING_POINT_BUFFER mysql-test/r/type_float.result: New test cases. mysql-test/t/type_float.test: New test cases. sql/sql_string.cc: Increase buffer size in String::set(double, ...) sql/unireg.h: Move definition of FLOATING_POINT_BUFFER --- sql/sql_string.cc | 6 ++++-- sql/unireg.h | 1 - 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'sql') diff --git a/sql/sql_string.cc b/sql/sql_string.cc index 1c9a3cd7fc2..545643de49f 100644 --- a/sql/sql_string.cc +++ b/sql/sql_string.cc @@ -117,7 +117,7 @@ bool String::set(ulonglong num, CHARSET_INFO *cs) bool String::set(double num,uint decimals, CHARSET_INFO *cs) { - char buff[331]; + char buff[FLOATING_POINT_BUFFER]; uint dummy_errors; str_charset=cs; @@ -186,7 +186,9 @@ end: #else #ifdef HAVE_SNPRINTF buff[sizeof(buff)-1]=0; // Safety - snprintf(buff,sizeof(buff)-1, "%.*f",(int) decimals,num); + int num_chars= snprintf(buff, sizeof(buff)-1, "%.*f",(int) decimals, num); + DBUG_ASSERT(num_chars > 0); + DBUG_ASSERT(num_chars < (int) sizeof(buff)); #else sprintf(buff,"%.*f",(int) decimals,num); #endif diff --git a/sql/unireg.h b/sql/unireg.h index b5518809527..dd79de0781a 100644 --- a/sql/unireg.h +++ b/sql/unireg.h @@ -175,7 +175,6 @@ */ #define BIN_LOG_HEADER_SIZE 4 -#define FLOATING_POINT_BUFFER 331 #define DEFAULT_KEY_CACHE_NAME "default" -- cgit v1.2.1 From 0fca2269425df6a1e2224bb3185a76d3a2608fbf Mon Sep 17 00:00:00 2001 From: Mattias Jonsson Date: Tue, 23 Aug 2011 15:13:17 +0200 Subject: Bug#11766879/Bug#60106: DIFF BETWEEN # OF INDEXES IN MYSQL VS INNODB, PARTITONING, ON INDEX CREATE If the first partition succeeded in adding a index, but a successive partition failed, then the first partition had still the new index. The fix reverts the added indexes from previous partitions on failure. --- sql/ha_partition.cc | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) (limited to 'sql') diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index cd5d7f201a7..7c7cf5a4302 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -6490,7 +6490,25 @@ int ha_partition::add_index(TABLE *table_arg, KEY *key_info, uint num_of_keys) */ for (file= m_file; *file; file++) if ((ret= (*file)->add_index(table_arg, key_info, num_of_keys))) - break; + goto err; + return ret; +err: + if (file > m_file) + { + uint *key_numbers= (uint*) ha_thd()->alloc(sizeof(uint) * num_of_keys); + KEY *old_key_info= table_arg->key_info; + uint i; + /* Use the newly added key_info as table->key_info to remove them. */ + for (i= 0; i < num_of_keys; i++) + key_numbers[i]= i; + table_arg->key_info= key_info; + while (--file >= m_file) + { + (void) (*file)->prepare_drop_index(table_arg, key_numbers, num_of_keys); + (void) (*file)->final_drop_index(table_arg); + } + table_arg->key_info= old_key_info; + } return ret; } -- cgit v1.2.1 From 1922d65fd8881836b57ac925596dac4cb753e29b Mon Sep 17 00:00:00 2001 From: Alexander Nozdrin Date: Thu, 22 Sep 2011 18:31:16 +0400 Subject: Fix for Bug#13001491: MYSQL_REFRESH CRASHES WHEN STORED ROUTINES ARE RUN CONCURRENTLY. The main problem was that lex_start() was forgotten to be called before processing COM_REFRESH. Another problem discovered was that if failures to flush the error log were not properly handled, which resulted in the server crash. The user-visible effect of these problems were: - if COM_REFRESH command was sent after SQL-queries of some sort, the server would crash. - if COM_REFRESH was requested with REFRESH_LOG only, and the error log failed to flush, the server would crash. The error log fails to flush when it points to unavailable file (for example, due to restricted permissions). The fixes are: - call lex_start() in the beginning of COM_REFRESH; - handle failures to flush the error log properly, i.e. raise ER_UNKNOWN_ERROR. sql/sql_parse.cc: Fix for Bug#13001491: MYSQL_REFRESH CRASHES WHEN STORED ROUTINES ARE RUN CONCURRENTLY. tests/mysql_client_test.c: A test case for Bug#13001491: MYSQL_REFRESH CRASHES WHEN STORED ROUTINES ARE RUN CONCURRENTLY. --- sql/sql_parse.cc | 44 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 39 insertions(+), 5 deletions(-) (limited to 'sql') diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 2eeabc2f0c1..b5864fef19d 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1491,6 +1491,14 @@ bool dispatch_command(enum enum_server_command command, THD *thd, case COM_REFRESH: { int not_used; + + /* + Initialize thd->lex since it's used in many base functions, such as + open_tables(). Otherwise, it remains unitialized and may cause crash + during execution of COM_REFRESH. + */ + lex_start(thd); + status_var_increment(thd->status_var.com_stat[SQLCOM_FLUSH]); ulong options= (ulong) (uchar) packet[0]; if (check_global_access(thd,RELOAD_ACL)) @@ -6986,7 +6994,14 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables, if (ha_flush_logs(NULL)) result=1; if (flush_error_log()) - result=1; + { + /* + When flush_error_log() failed, my_error() has not been called. + So, we have to do it here to keep the protocol. + */ + my_error(ER_UNKNOWN_ERROR, MYF(0)); + result= 1; + } } #ifdef HAVE_QUERY_CACHE if (options & REFRESH_QUERY_CACHE_FREE) @@ -7035,7 +7050,13 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables, return 1; // Killed if (close_cached_tables(thd, tables, FALSE, (options & REFRESH_FAST) ? FALSE : TRUE, TRUE)) - result= 1; + { + /* + NOTE: my_error() has been already called by reopen_tables() within + close_cached_tables(). + */ + result= 1; + } if (make_global_read_lock_block_commit(thd)) // Killed { @@ -7048,7 +7069,13 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables, { if (close_cached_tables(thd, tables, FALSE, (options & REFRESH_FAST) ? FALSE : TRUE, FALSE)) + { + /* + NOTE: my_error() has been already called by reopen_tables() within + close_cached_tables(). + */ result= 1; + } } my_dbopt_cleanup(); } @@ -7065,7 +7092,8 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables, tmp_write_to_binlog= 0; if (reset_master(thd)) { - result=1; + /* NOTE: my_error() has been already called by reset_master(). */ + result= 1; } } #endif @@ -7073,7 +7101,10 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables, if (options & REFRESH_DES_KEY_FILE) { if (des_key_file && load_des_key_file(des_key_file)) - result= 1; + { + /* NOTE: my_error() has been already called by load_des_key_file(). */ + result= 1; + } } #endif #ifdef HAVE_REPLICATION @@ -7082,7 +7113,10 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables, tmp_write_to_binlog= 0; pthread_mutex_lock(&LOCK_active_mi); if (reset_slave(thd, active_mi)) - result=1; + { + /* NOTE: my_error() has been already called by reset_slave(). */ + result= 1; + } pthread_mutex_unlock(&LOCK_active_mi); } #endif -- cgit v1.2.1 From ffd0a785f4e235c5fc633467b793686a79cf5237 Mon Sep 17 00:00:00 2001 From: Raghav Kapoor Date: Wed, 28 Sep 2011 15:39:21 +0530 Subject: BUG#11758062 - 50206: ER_TOO_BIG_SELECT REFERS TO OUTMODED SYSTEM VARIABLE NAME SQL_MAX_JOIN_SI BACKGROUND: ER_TOO_BIG_SELECT refers to SQL_MAX_JOIN_SIZE, which is the old name for MAX_JOIN_SIZE. FIX: Support for old name SQL_MAX_JOIN_SIZE is removed in MySQL 5.6 and is renamed as MAX_JOIN_SIZE.So the errmsg.txt and mysql.cc files have been updated and the corresponding result files have also been updated. --- sql/share/errmsg.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sql') diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt index bbae17c4327..7cb9d9ebcc1 100644 --- a/sql/share/errmsg.txt +++ b/sql/share/errmsg.txt @@ -2494,10 +2494,10 @@ ER_TOO_BIG_SELECT 42000 cze "Zadan-Bý SELECT by procházel pøíli¹ mnoho záznamù a trval velmi dlouho. Zkontrolujte tvar WHERE a je-li SELECT v poøádku, pou¾ijte SET SQL_BIG_SELECTS=1" dan "SELECT ville undersøge for mange poster og ville sandsynligvis tage meget lang tid. Undersøg WHERE delen og brug SET SQL_BIG_SELECTS=1 hvis udtrykket er korrekt" nla "Het SELECT-statement zou te veel records analyseren en dus veel tijd in beslagnemen. Kijk het WHERE-gedeelte van de query na en kies SET SQL_BIG_SELECTS=1 als het stament in orde is." - eng "The SELECT would examine more than MAX_JOIN_SIZE rows; check your WHERE and use SET SQL_BIG_SELECTS=1 or SET SQL_MAX_JOIN_SIZE=# if the SELECT is okay" + eng "The SELECT would examine more than MAX_JOIN_SIZE rows; check your WHERE and use SET SQL_BIG_SELECTS=1 or SET MAX_JOIN_SIZE=# if the SELECT is okay" est "SELECT lause peab läbi vaatama suure hulga kirjeid ja võtaks tõenäoliselt liiga kaua aega. Tasub kontrollida WHERE klauslit ja vajadusel kasutada käsku SET SQL_BIG_SELECTS=1" fre "SELECT va devoir examiner beaucoup d'enregistrements ce qui va prendre du temps. Vérifiez la clause WHERE et utilisez SET SQL_BIG_SELECTS=1 si SELECT se passe bien" - ger "Die Ausführung des SELECT würde zu viele Datensätze untersuchen und wahrscheinlich sehr lange dauern. Bitte WHERE-Klausel überprüfen und gegebenenfalls SET SQL_BIG_SELECTS=1 oder SET SQL_MAX_JOIN_SIZE=# verwenden" + ger "Die Ausführung des SELECT würde zu viele Datensätze untersuchen und wahrscheinlich sehr lange dauern. Bitte WHERE-Klausel überprüfen und gegebenenfalls SET SQL_BIG_SELECTS=1 oder SET MAX_JOIN_SIZE=# verwenden" greek "Ôï SELECT èá åîåôÜóåé ìåãÜëï áñéèìü åããñáöþí êáé ðéèáíþò èá êáèõóôåñÞóåé. Ðáñáêáëþ åîåôÜóôå ôéò ðáñáìÝôñïõò ôïõ WHERE êáé ÷ñçóéìïðïéåßóôå SET SQL_BIG_SELECTS=1 áí ôï SELECT åßíáé óùóôü" hun "A SELECT tul sok rekordot fog megvizsgalni es nagyon sokaig fog tartani. Ellenorizze a WHERE-t es hasznalja a SET SQL_BIG_SELECTS=1 beallitast, ha a SELECT okay" ita "La SELECT dovrebbe esaminare troppi record e usare troppo tempo. Controllare la WHERE e usa SET SQL_BIG_SELECTS=1 se e` tutto a posto." -- cgit v1.2.1 From 8932ae216674f08880a6f914b9651262037175be Mon Sep 17 00:00:00 2001 From: Tatjana Azundris Nuernberg Date: Thu, 29 Sep 2011 10:47:11 +0100 Subject: Bug#11765687 (MySQL58677): No privilege on table / view, but can know #rows / underlying table's name 1 - If a user had SHOW VIEW and SELECT privileges on a view and this view was referencing another view, EXPLAIN SELECT on the outer view (that the user had privileges on) could reveal the structure of the underlying "inner" view as well as the number of rows in the underlying tables, even if the user had privileges on none of these referenced objects. This happened because we used DEFINER's UID ("SUID") not just for the view given in EXPLAIN, but also when checking privileges on the underlying views (where we should use the UID of the EXPLAIN's INVOKER instead). We no longer run the EXPLAIN SUID (with DEFINER's privileges). This prevents a possible exploit and makes permissions more orthogonal. 2 - EXPLAIN SELECT would reveal a view's structure even if the user did not have SHOW VIEW privileges for that view, as long as they had SELECT privilege on the underlying tables. Instead of requiring both SHOW VIEW privilege on a view and SELECT privilege on all underlying tables, we were checking for presence of either of them. We now explicitly require SHOW VIEW and SELECT privileges on the view we run EXPLAIN SELECT on, as well as all its underlying views. We also require SELECT on all relevant tables. mysql-test/r/view_grant.result: add extensive tests to illustrate desired behavior and prevent regressions (as always). mysql-test/t/view_grant.test: add extensive tests to illustrate desired behavior and prevent regressions (as always). sql/sql_view.cc: We no longer run the EXPLAIN SUID (with DEFINER's privileges). To achieve this, we use a temporary, SUID-less TABLE_LIST for the views while checking privileges. --- sql/sql_view.cc | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) (limited to 'sql') diff --git a/sql/sql_view.cc b/sql/sql_view.cc index 07580663f24..c318f79eec1 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -1148,8 +1148,38 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table, if (!table->prelocking_placeholder && (old_lex->sql_command == SQLCOM_SELECT && old_lex->describe)) { - if (check_table_access(thd, SELECT_ACL, view_tables, 1) && - check_table_access(thd, SHOW_VIEW_ACL, table, 1)) + /* + The user we run EXPLAIN as (either the connected user who issued + the EXPLAIN statement, or the definer of a SUID stored routine + which contains the EXPLAIN) should have both SHOW_VIEW_ACL and + SELECT_ACL on the view being opened as well as on all underlying + views since EXPLAIN will disclose their structure. This user also + should have SELECT_ACL on all underlying tables of the view since + this EXPLAIN will disclose information about the number of rows in it. + + To perform this privilege check we create auxiliary TABLE_LIST object + for the view in order a) to avoid trashing "table->grant" member for + original table list element, which contents can be important at later + stage for column-level privilege checking b) get TABLE_LIST object + with "security_ctx" member set to 0, i.e. forcing check_table_access() + to use active user's security context. + + There is no need for creating similar copies of table list elements + for underlying tables since they are just have been constructed and + thus have TABLE_LIST::security_ctx == 0 and fresh TABLE_LIST::grant + member. + + Finally at this point making sure we have SHOW_VIEW_ACL on the views + will suffice as we implicitly require SELECT_ACL anyway. + */ + + TABLE_LIST view; + bzero((char *)&view, sizeof(TABLE_LIST)); + view.db= table->db; + view.table_name= table->table_name; + + if (check_table_access(thd, SELECT_ACL, view_tables, 1) || + check_table_access(thd, SHOW_VIEW_ACL, &view, 1)) { my_message(ER_VIEW_NO_EXPLAIN, ER(ER_VIEW_NO_EXPLAIN), MYF(0)); goto err; -- cgit v1.2.1 From 14dc91ff83001809fc40380ab22fc4e606f611de Mon Sep 17 00:00:00 2001 From: Sergey Glukhov Date: Wed, 5 Oct 2011 13:28:20 +0400 Subject: Bug#11747970 34660: CRASH WHEN FEDERATED TABLE LOSES CONNECTION DURING INSERT ... SELECT Problematic query: insert ignore into `t1_federated` (`c1`) select `c1` from `t1_local` a where not exists (select 1 from `t1_federated` b where a.c1 = b.c1); When this query is killed in another connection it could lead to crash. The problem is follwing: An attempt to obtain table statistics for subselect table in killed query fails with an error. So JOIN::optimize() for subquery is failed but it does not prevent further subquery evaluation. At the first subquery execution JOIN::optimize() is called (see subselect_single_select_engine::exec()) and fails with an error. 'executed' flag is set to TRUE and it prevents further subquery evaluation. At the second call JOIN::optimize() does not happen as 'JOIN::optimized' is TRUE and in case of uncacheable subquery the 'executed' flag is set to FALSE before subquery evaluation. So we loose 'optimize stage' error indication (see subselect_single_select_engine::exec()). In other words 'executed' flag is used for two purposes, for error indication at JOIN::optimize() stage and for an indication of subquery execution. And it seems it's wrong as the flag could be reset. mysql-test/r/error_simulation.result: test case mysql-test/t/error_simulation.test: test case sql/item_subselect.cc: added new flag subselect_single_select_engine::optimize_error which is used for error detection which could happen at optimize stage. sql/item_subselect.h: added new flag subselect_single_select_engine::optimize_error sql/sql_select.cc: test case --- sql/item_subselect.cc | 13 ++++++++++--- sql/item_subselect.h | 1 + sql/sql_select.cc | 10 ++++++++++ 3 files changed, 21 insertions(+), 3 deletions(-) (limited to 'sql') diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 10dd6c93717..2fa0178ed8c 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -1679,7 +1679,7 @@ subselect_single_select_engine(st_select_lex *select, select_subselect *result_arg, Item_subselect *item_arg) :subselect_engine(item_arg, result_arg), - prepared(0), optimized(0), executed(0), + prepared(0), optimized(0), executed(0), optimize_error(0), select_lex(select), join(0) { select_lex->master_unit()->item= item_arg; @@ -1689,7 +1689,7 @@ subselect_single_select_engine(st_select_lex *select, void subselect_single_select_engine::cleanup() { DBUG_ENTER("subselect_single_select_engine::cleanup"); - prepared= optimized= executed= 0; + prepared= optimized= executed= optimize_error= 0; join= 0; result->cleanup(); DBUG_VOID_RETURN; @@ -1885,6 +1885,10 @@ int join_read_next_same_or_null(READ_RECORD *info); int subselect_single_select_engine::exec() { DBUG_ENTER("subselect_single_select_engine::exec"); + + if (optimize_error) + DBUG_RETURN(1); + char const *save_where= thd->where; SELECT_LEX *save_select= thd->lex->current_select; thd->lex->current_select= select_lex; @@ -1892,12 +1896,15 @@ int subselect_single_select_engine::exec() { SELECT_LEX_UNIT *unit= select_lex->master_unit(); + DBUG_EXECUTE_IF("bug11747970_simulate_error", + DBUG_SET("+d,bug11747970_raise_error");); + optimized= 1; unit->set_limit(unit->global_parameters); if (join->optimize()) { thd->where= save_where; - executed= 1; + optimize_error= 1; thd->lex->current_select= save_select; DBUG_RETURN(join->error ? join->error : 1); } diff --git a/sql/item_subselect.h b/sql/item_subselect.h index 74bbec626c1..65b415b44e9 100644 --- a/sql/item_subselect.h +++ b/sql/item_subselect.h @@ -419,6 +419,7 @@ class subselect_single_select_engine: public subselect_engine my_bool prepared; /* simple subselect is prepared */ my_bool optimized; /* simple subselect is optimized */ my_bool executed; /* simple subselect is executed */ + my_bool optimize_error; ///< simple subselect optimization failed st_select_lex *select_lex; /* corresponding select_lex */ JOIN * join; /* corresponding JOIN structure */ public: diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 516c9c37473..bf0cd7c9db8 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -2677,6 +2677,16 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables_arg, COND *conds, table_vector[i]=s->table=table=tables->table; table->pos_in_table_list= tables; error= table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK); + + DBUG_EXECUTE_IF("bug11747970_raise_error", + { + if (!error) + { + my_error(ER_UNKNOWN_ERROR, MYF(0)); + goto error; + } + }); + if (error) { table->file->print_error(error, MYF(0)); -- cgit v1.2.1 From d740f9e603378c2ba9bbfacd111f6fe9c6728b78 Mon Sep 17 00:00:00 2001 From: Tatjana Azundris Nuernberg Date: Thu, 6 Oct 2011 11:23:46 +0100 Subject: additional clean-up for 11765687 --- sql/sql_view.cc | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) (limited to 'sql') diff --git a/sql/sql_view.cc b/sql/sql_view.cc index c318f79eec1..3ae35e5cfe0 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -1164,22 +1164,23 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table, with "security_ctx" member set to 0, i.e. forcing check_table_access() to use active user's security context. - There is no need for creating similar copies of table list elements - for underlying tables since they are just have been constructed and - thus have TABLE_LIST::security_ctx == 0 and fresh TABLE_LIST::grant - member. + There is no need for creating similar copies of TABLE_LIST elements + for underlying tables since they just have been constructed and thus + have TABLE_LIST::security_ctx == 0 and fresh TABLE_LIST::grant member. Finally at this point making sure we have SHOW_VIEW_ACL on the views will suffice as we implicitly require SELECT_ACL anyway. */ - TABLE_LIST view; - bzero((char *)&view, sizeof(TABLE_LIST)); - view.db= table->db; - view.table_name= table->table_name; + TABLE_LIST view_no_suid; + bzero(static_cast(&view_no_suid), sizeof(TABLE_LIST)); + view_no_suid.db= table->db; + view_no_suid.table_name= table->table_name; + + DBUG_ASSERT(view_tables == NULL || view_tables->security_ctx == NULL); if (check_table_access(thd, SELECT_ACL, view_tables, 1) || - check_table_access(thd, SHOW_VIEW_ACL, &view, 1)) + check_table_access(thd, SHOW_VIEW_ACL, &view_no_suid, 1)) { my_message(ER_VIEW_NO_EXPLAIN, ER(ER_VIEW_NO_EXPLAIN), MYF(0)); goto err; -- cgit v1.2.1 From f36e854ac6ce19e7018addbb8701796006a27134 Mon Sep 17 00:00:00 2001 From: Magne Mahre Date: Fri, 7 Oct 2011 14:08:31 +0200 Subject: BUG#12589870 CRASHES WITH MULTIQUERY PACKET + USE + QUERY CACHE A buffer large enough to hold the query _plus_ some additional data is allocated before parsing is started. The additional data is used by the query cache, and consists of the name of the current database and a set of flags. When a packet containing multiple SQL statements is sent to the server and one of the statements changes the current database (a "USE " statement), and the name of the new current database is longer than of the previous, there is not enough space in the buffer for the new name, and we write out over the buffer boundary. The fix adds an extra field to store the number of bytes allocated to the database name in the buffer. If the current database name changes, and the new name is longer than the previous one, we refuse to cache the query. --- sql/sp_head.cc | 12 +++++++++++- sql/sql_cache.cc | 33 ++++++++++++++++++++++++++++----- sql/sql_parse.cc | 21 +++++++++++++++++++-- 3 files changed, 58 insertions(+), 8 deletions(-) (limited to 'sql') diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 95a7eda0540..70ffb0a36ab 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -1029,12 +1029,22 @@ subst_spvars(THD *thd, sp_instr *instr, LEX_STRING *query_str) /* Allocate additional space at the end of the new query string for the query_cache_send_result_to_client function. + + The query buffer layout is: + buffer :== + The input statement(s) + '\0' Terminating null char + Length of following current database name (size_t) + Name of current database + Flags struct */ - buf_len= qbuf.length() + thd->db_length + 1 + QUERY_CACHE_FLAGS_SIZE + 1; + buf_len= qbuf.length() + 1 + sizeof(size_t) + thd->db_length + + QUERY_CACHE_FLAGS_SIZE + 1; if ((pbuf= (char *) alloc_root(thd->mem_root, buf_len))) { memcpy(pbuf, qbuf.ptr(), qbuf.length()); pbuf[qbuf.length()]= 0; + *(size_t *)(pbuf+qbuf.length()+1)= thd->db_length; } else DBUG_RETURN(TRUE); diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc index b791428eef0..4800fdedbe5 100644 --- a/sql/sql_cache.cc +++ b/sql/sql_cache.cc @@ -1241,8 +1241,8 @@ def_week_frmt: %lu, in_trans: %d, autocommit: %d", /* Key is query + database + flag */ if (thd->db_length) { - memcpy(thd->query() + thd->query_length() + 1, thd->db, - thd->db_length); + memcpy(thd->query() + thd->query_length() + 1 + sizeof(size_t), + thd->db, thd->db_length); DBUG_PRINT("qcache", ("database: %s length: %u", thd->db, (unsigned) thd->db_length)); } @@ -1251,7 +1251,7 @@ def_week_frmt: %lu, in_trans: %d, autocommit: %d", DBUG_PRINT("qcache", ("No active database")); } tot_length= thd->query_length() + thd->db_length + 1 + - QUERY_CACHE_FLAGS_SIZE; + sizeof(size_t) + QUERY_CACHE_FLAGS_SIZE; /* We should only copy structure (don't use it location directly) because of alignment issue @@ -1462,7 +1462,28 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length) goto err; } } + { + /* + We have allocated buffer space (in alloc_query) to hold the + SQL statement(s) + the current database name + a flags struct. + If the database name has changed during execution, which might + happen if there are multiple statements, we need to make + sure the new current database has a name with the same length + as the previous one. + */ + size_t *db_len= (size_t *) (sql + query_length + 1); + if (thd->db_length != *db_len) + { + /* + We should probably reallocate the buffer in this case, + but for now we just leave it uncached + */ + DBUG_PRINT("qcache", + ("Current database has changed since start of query")); + goto err; + } + } /* Try to obtain an exclusive lock on the query cache. If the cache is disabled or if a full cache flush is in progress, the attempt to @@ -1484,10 +1505,12 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length) Query_cache_block *query_block; - tot_length= query_length + thd->db_length + 1 + QUERY_CACHE_FLAGS_SIZE; + tot_length= query_length + 1 + sizeof(size_t) + + thd->db_length + QUERY_CACHE_FLAGS_SIZE; + if (thd->db_length) { - memcpy(sql+query_length+1, thd->db, thd->db_length); + memcpy(sql + query_length + 1 + sizeof(size_t), thd->db, thd->db_length); DBUG_PRINT("qcache", ("database: '%s' length: %u", thd->db, (unsigned)thd->db_length)); } diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index b5864fef19d..55509a31f77 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1912,13 +1912,30 @@ bool alloc_query(THD *thd, const char *packet, uint packet_length) pos--; packet_length--; } - /* We must allocate some extra memory for query cache */ + /* We must allocate some extra memory for query cache + + The query buffer layout is: + buffer :== + The input statement(s) + '\0' Terminating null char (1 byte) + Length of following current database name (size_t) + Name of current database + Flags struct + */ if (! (query= (char*) thd->memdup_w_gap(packet, packet_length, - 1 + thd->db_length + + 1 + sizeof(size_t) + thd->db_length + QUERY_CACHE_FLAGS_SIZE))) return TRUE; query[packet_length]= '\0'; + /* + Space to hold the name of the current database is allocated. We + also store this length, in case current database is changed during + execution. We might need to reallocate the 'query' buffer + */ + size_t *len = (size_t *) (query + packet_length + 1); + *len= thd->db_length; + thd->set_query(query, packet_length); /* Reclaim some memory */ -- cgit v1.2.1 From a6145f4b62bd264ad32d2dade98eda0cc6de0dba Mon Sep 17 00:00:00 2001 From: Tor Didriksen Date: Fri, 14 Oct 2011 10:09:53 +0200 Subject: Bug#12563865 ROUNDED,TMP_BUF,DECIMAL_VALUE STACK CORRUPTION IN ALL VERSIONS >=5.0 Buffer over-run on all platforms, crash on windows, wrong result on other platforms, when rounding numbers which start with 999999999 and have precision = 9 or 18 or 27 or 36 ... mysql-test/r/type_newdecimal.result: New test cases. mysql-test/t/type_newdecimal.test: New test cases. sql/my_decimal.h: Add sanity checking code, to catch buffer over/under-run. strings/decimal.c: The original initialization of intg1 (add 1 if buf[0] == DIG_MAX) will set p1 to point outside the buffer, and the loop to copy the original value while (buf0 < p0) *(--p1) = *(--p0); will overwrite memory outside the my_decimal object. --- sql/my_decimal.h | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) (limited to 'sql') diff --git a/sql/my_decimal.h b/sql/my_decimal.h index 6a0d05921ec..ee023438f20 100644 --- a/sql/my_decimal.h +++ b/sql/my_decimal.h @@ -86,12 +86,31 @@ inline int my_decimal_int_part(uint precision, uint decimals) class my_decimal :public decimal_t { + /* + Several of the routines in strings/decimal.c have had buffer + overrun/underrun problems. These are *not* caught by valgrind. + To catch them, we allocate dummy fields around the buffer, + and test that their values do not change. + */ +#if !defined(DBUG_OFF) + int foo1; +#endif + decimal_digit_t buffer[DECIMAL_BUFF_LENGTH]; +#if !defined(DBUG_OFF) + int foo2; + static const int test_value= 123; +#endif + public: void init() { +#if !defined(DBUG_OFF) + foo1= test_value; + foo2= test_value; +#endif len= DECIMAL_BUFF_LENGTH; buf= buffer; #if !defined (HAVE_purify) && !defined(DBUG_OFF) @@ -104,6 +123,17 @@ public: { init(); } + ~my_decimal() + { + sanity_check(); + } + + void sanity_check() + { + DBUG_ASSERT(foo1 == test_value); + DBUG_ASSERT(foo2 == test_value); + } + void fix_buffer_pointer() { buf= buffer; } bool sign() const { return decimal_t::sign; } -- cgit v1.2.1 From 8444b6a1149aad457f8d584e0d410c3be9cd4336 Mon Sep 17 00:00:00 2001 From: Tatjana Azundris Nuernberg Date: Wed, 19 Oct 2011 03:21:31 +0100 Subject: Bug12589870 post-merge fixes for Sparc64 and friends sql/sp_head.cc: alignment-safe copy sql/sql_cache.cc: alignment-safe copy sql/sql_parse.cc: alignment-safe copy --- sql/sp_head.cc | 2 +- sql/sql_cache.cc | 5 +++-- sql/sql_parse.cc | 6 ++++-- 3 files changed, 8 insertions(+), 5 deletions(-) (limited to 'sql') diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 70ffb0a36ab..8bea0be0f56 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -1044,7 +1044,7 @@ subst_spvars(THD *thd, sp_instr *instr, LEX_STRING *query_str) { memcpy(pbuf, qbuf.ptr(), qbuf.length()); pbuf[qbuf.length()]= 0; - *(size_t *)(pbuf+qbuf.length()+1)= thd->db_length; + memcpy(pbuf+qbuf.length()+1, (char *) &thd->db_length, sizeof(size_t)); } else DBUG_RETURN(TRUE); diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc index 4800fdedbe5..8a6d4bf9802 100644 --- a/sql/sql_cache.cc +++ b/sql/sql_cache.cc @@ -1471,8 +1471,9 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length) sure the new current database has a name with the same length as the previous one. */ - size_t *db_len= (size_t *) (sql + query_length + 1); - if (thd->db_length != *db_len) + size_t db_len; + memcpy((char *) &db_len, (sql + query_length + 1), sizeof(size_t)); + if (thd->db_length != db_len) { /* We should probably reallocate the buffer in this case, diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index a88cc2fa8e5..989c3e0f42f 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -492,6 +492,8 @@ static void handle_bootstrap_impl(THD *thd) query= (char *) thd->memdup_w_gap(buff, length + 1, thd->db_length + 1 + QUERY_CACHE_FLAGS_SIZE); + size_t db_len= 0; + memcpy(query + length + 1, (char *) &db_len, sizeof(size_t)); thd->set_query(query, length); DBUG_PRINT("query",("%-.4096s", thd->query())); #if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER) @@ -1933,8 +1935,8 @@ bool alloc_query(THD *thd, const char *packet, uint packet_length) also store this length, in case current database is changed during execution. We might need to reallocate the 'query' buffer */ - size_t *len = (size_t *) (query + packet_length + 1); - *len= thd->db_length; + char *len_pos = (query + packet_length + 1); + memcpy(len_pos, (char *) &thd->db_length, sizeof(size_t)); thd->set_query(query, packet_length); -- cgit v1.2.1 From dce337406ea07b4581bf724133d5262fddd365f3 Mon Sep 17 00:00:00 2001 From: Vasil Dimov Date: Tue, 25 Oct 2011 16:46:38 +0300 Subject: Fix Bug#12661768 UPDATE IGNORE CRASHES SERVER IF TABLE IS INNODB AND IT IS PARENT FOR OTHER ONE Do not try to lookup key_nr'th key in 'table' because there may not be such a key there. key_nr is the number of the key in the _child_ table name, not in the parent table. Instead just print the fields of the record that are covered by the first key defined on the parent table. This bug gets a better fix in MySQL 5.6, which is too risky for 5.1 and 5.5. Approved by: Jon Olav Hauglid (via IM) --- sql/handler.cc | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'sql') diff --git a/sql/handler.cc b/sql/handler.cc index 82f5f2ee841..895e7cc4df0 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -2692,7 +2692,13 @@ void handler::print_error(int error, myf errflag) char key[MAX_KEY_LENGTH]; String str(key,sizeof(key),system_charset_info); /* Table is opened and defined at this point */ - key_unpack(&str,table,(uint) key_nr); + key_unpack(&str,table,0 /* Use 0 instead of key_nr because key_nr + is a key number in the child FK table, not in our 'table'. See + Bug#12661768 UPDATE IGNORE CRASHES SERVER IF TABLE IS INNODB + AND IT IS PARENT FOR OTHER ONE + This bug gets a better fix in MySQL 5.6, but it is too risky + to get that in 5.1 and 5.5 (extending the handler interface + and adding new error message codes */); max_length= (MYSQL_ERRMSG_SIZE- (uint) strlen(ER(ER_FOREIGN_DUPLICATE_KEY))); if (str.length() >= max_length) -- cgit v1.2.1 From 2e7dd62646b643a86e17dae6fb58a69822a68c1f Mon Sep 17 00:00:00 2001 From: Tor Didriksen Date: Mon, 31 Oct 2011 10:10:04 +0100 Subject: Bug#12406055 post-push fix: unused variable 'num_chars' in optimized build. --- sql/sql_string.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'sql') diff --git a/sql/sql_string.cc b/sql/sql_string.cc index ea146c3938c..4dbc2d77206 100644 --- a/sql/sql_string.cc +++ b/sql/sql_string.cc @@ -200,7 +200,8 @@ end: #else #ifdef HAVE_SNPRINTF buff[sizeof(buff)-1]=0; // Safety - int num_chars= snprintf(buff, sizeof(buff)-1, "%.*f",(int) decimals, num); + IF_DBUG(int num_chars= ) + snprintf(buff, sizeof(buff)-1, "%.*f",(int) decimals, num); DBUG_ASSERT(num_chars > 0); DBUG_ASSERT(num_chars < (int) sizeof(buff)); #else -- cgit v1.2.1 From 0e2af2cdd32bc2482483c09ac8dd9c0aa7549c15 Mon Sep 17 00:00:00 2001 From: Tor Didriksen Date: Tue, 1 Nov 2011 07:50:54 +0100 Subject: Bug#12406055 post-push fix: unused variable 'num_chars' in optimized build. Also fixed possibly uninitialized use of need_copy_table_res. --- sql/sql_table.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sql') diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 105b5e7a43c..b7feed5ab4c 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -6985,7 +6985,7 @@ view_err: need_copy_table= ALTER_TABLE_DATA_CHANGED; else { - enum_alter_table_change_level need_copy_table_res; + enum_alter_table_change_level need_copy_table_res=ALTER_TABLE_METADATA_ONLY; /* Check how much the tables differ. */ if (compare_tables(table, alter_info, create_info, order_num, -- cgit v1.2.1 From a0a51251e49365cc0f256a2ebc90994dbe0d9a95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Thu, 10 Nov 2011 12:49:31 +0200 Subject: Bug#11759688 52020: InnoDB can still deadlock on just INSERT...ON DUPLICATE KEY a.k.a. Bug#7975 deadlock without any locking, simple select and update Bug#7975 was reintroduced when the storage engine API was made pluggable in MySQL 5.1. Instead of looking at thd->lex directly, we rely on handler::extra(). But, we were looking at the wrong extra() flag, and we were ignoring the TRX_DUP_REPLACE flag in places where we should obey it. innodb_replace.test: Add tests for hopefully all affected statement types, so that bug should never ever resurface. This kind of tests should have been added when fixing Bug#7975 in MySQL 5.0.3 in the first place. rb:806 approved by Sunny Bains --- sql/sql_insert.cc | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'sql') diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index e176e5c9b6d..4cd456829ba 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -63,6 +63,8 @@ #include "slave.h" #include "rpl_mi.h" +#include "debug_sync.h" + #ifndef EMBEDDED_LIBRARY static bool delayed_get_table(THD *thd, TABLE_LIST *table_list); static int write_delayed(THD *thd, TABLE *table, enum_duplicates duplic, @@ -1411,6 +1413,8 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info) error= HA_ERR_FOUND_DUPP_KEY; /* Database can't find key */ goto err; } + DEBUG_SYNC(thd, "write_row_replace"); + /* Read all columns for the row we are going to replace */ table->use_all_columns(); /* @@ -1604,6 +1608,7 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info) } else if ((error=table->file->ha_write_row(table->record[0]))) { + DEBUG_SYNC(thd, "write_row_noreplace"); if (!info->ignore || table->file->is_fatal_error(error, HA_CHECK_DUP)) goto err; -- cgit v1.2.1 From 523c849d14acf041670337afa6a7e2a6deeaab67 Mon Sep 17 00:00:00 2001 From: Jorgen Loland Date: Fri, 18 Nov 2011 14:47:11 +0100 Subject: Backmerge of BUG#12997905 --- sql/field.h | 17 +++++++++++++++++ sql/field_conv.cc | 2 +- sql/sql_select.cc | 3 +++ 3 files changed, 21 insertions(+), 1 deletion(-) (limited to 'sql') diff --git a/sql/field.h b/sql/field.h index 8db965a8270..ca23702fd8c 100644 --- a/sql/field.h +++ b/sql/field.h @@ -2128,6 +2128,23 @@ public: uchar *from_null_ptr,*to_null_ptr; my_bool *null_row; uint from_bit,to_bit; + /** + Number of bytes in the fields pointed to by 'from_ptr' and + 'to_ptr'. Usually this is the number of bytes that are copied from + 'from_ptr' to 'to_ptr'. + + For variable-length fields (VARCHAR), the first byte(s) describe + the actual length of the text. For VARCHARs with length + < 256 there is 1 length byte + >= 256 there is 2 length bytes + Thus, if from_field is VARCHAR(10), from_length (and in most cases + to_length) is 11. For VARCHAR(1024), the length is 1026. @see + Field_varstring::length_bytes + + Note that for VARCHARs, do_copy() will be do_varstring*() which + only copies the length-bytes (1 or 2) + the actual length of the + text instead of from/to_length bytes. @see get_copy_func() + */ uint from_length,to_length; Field *from_field,*to_field; String tmp; // For items diff --git a/sql/field_conv.cc b/sql/field_conv.cc index 1dc7ec57944..ff56a1fdda8 100644 --- a/sql/field_conv.cc +++ b/sql/field_conv.cc @@ -707,7 +707,7 @@ Copy_field::get_copy_func(Field *to,Field *from) if (((Field_varstring*) to)->length_bytes != ((Field_varstring*) from)->length_bytes) return do_field_string; - if (to_length != from_length) + else return (((Field_varstring*) to)->length_bytes == 1 ? (from->charset()->mbmaxlen == 1 ? do_varstring1 : do_varstring1_mb) : diff --git a/sql/sql_select.cc b/sql/sql_select.cc index bf0cd7c9db8..72c826ff32d 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -10647,6 +10647,9 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List &fields, if (open_tmp_table(table)) goto err; + // Make empty record so random data is not written to disk + empty_record(table); + thd->mem_root= mem_root_save; DBUG_RETURN(table); -- cgit v1.2.1