From 0678a68bd0edeaa48a6e912d5a0bf3b65fb78fb0 Mon Sep 17 00:00:00 2001 From: Andrei Elkin Date: Mon, 10 Sep 2012 17:32:04 +0300 Subject: Bug#14597605 Issue with Null-value user on slave An "orthographic" typo in User_var::set_deferred() was made in fixes for bug@14275000. While editing the signature of the initial patch to remove the only argument, the assigned value of the argument remained in the body ... to be successfully compiled (!) thanks to names coincidence: the arg to User_var method and its member. Fixed with correcting the typo. --- sql/log_event.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sql') diff --git a/sql/log_event.h b/sql/log_event.h index 5030e1c6f3d..6b411a90382 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -2508,7 +2508,7 @@ public: and which case the applier adjusts execution path. */ bool is_deferred() { return deferred; } - void set_deferred() { deferred= val; } + void set_deferred() { deferred= true; } #endif bool is_valid() const { return 1; } -- cgit v1.2.1 From f0b52a9e7e26b761e14911a7a072d4cf91ceab54 Mon Sep 17 00:00:00 2001 From: Tor Didriksen Date: Wed, 12 Sep 2012 08:36:12 +0200 Subject: Backport Bug#13724099 --- sql/sql_list.h | 11 +++++++++-- sql/sql_select.cc | 17 ++++++++++++----- 2 files changed, 21 insertions(+), 7 deletions(-) (limited to 'sql') diff --git a/sql/sql_list.h b/sql/sql_list.h index 81283a6ae53..2eef291d948 100644 --- a/sql/sql_list.h +++ b/sql/sql_list.h @@ -1,7 +1,6 @@ #ifndef INCLUDES_MYSQL_SQL_LIST_H #define INCLUDES_MYSQL_SQL_LIST_H -/* - Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2000, 2012, 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 @@ -165,6 +164,14 @@ protected: public: uint elements; + bool operator==(const base_list &rhs) const + { + return + elements == rhs.elements && + first == rhs.first && + last == rhs.last; + } + inline void empty() { elements=0; first= &end_of_list; last=&first;} inline base_list() { empty(); } /** diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 042e7563d42..bcf601e5142 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2000, 2012, 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 @@ -1676,6 +1676,8 @@ JOIN::optimize() */ void JOIN::restore_tmp() { + DBUG_PRINT("info", ("restore_tmp this %p tmp_join %p", this, tmp_join)); + DBUG_ASSERT(tmp_join != this); memcpy(tmp_join, this, (size_t) sizeof(JOIN)); } @@ -7090,13 +7092,18 @@ void JOIN::cleanup(bool full) { if (tmp_join) tmp_table_param.copy_field= 0; - group_fields.delete_elements(); + /* - Ensure that the above delete_elements() would not be called + Ensure that the following delete_elements() would not be called twice for the same list. */ - if (tmp_join && tmp_join != this) - tmp_join->group_fields= group_fields; + if (tmp_join && tmp_join != this && + tmp_join->group_fields == this->group_fields) + tmp_join->group_fields.empty(); + + // Run Cached_item DTORs! + group_fields.delete_elements(); + /* We can't call delete_elements() on copy_funcs as this will cause problems in free_elements() as some of the elements are then deleted. -- cgit v1.2.1 From 5cbdb908270a052284155cd5b7eff7eacb177218 Mon Sep 17 00:00:00 2001 From: Sujatha Sivakumar Date: Mon, 17 Sep 2012 11:48:02 +0530 Subject: Bug#11750014:ASSERTION TRX_DATA->EMPTY() IN BINLOG_CLOSE_CONNECTION Problem: ======= trx_data->empty() assert happens at `binlog_close_connection' Analysis: ======== trx_data->empty() function checks for no pending events and the transaction cache to be empty.This function returns "true" if no pending events are present and cache is empty. Otherwise it returns false. `binlog_close_connection' call expects the above function to return true. But if the return value is false then assert is raised. This bug was reproducible in a diskfull scenario. In this disk full scenario try to do an insert operation so that a new pending event is created and flushing this pending event fails. Due to this failure the server goes down and invokes `binlog_close_connection' for clean closure. Since the pending event still remains the assert is caused. This assert is caused only in non transactional databases. Fix: === In a disk full scenario when the insertion fails the transaction is rolled back and `binlog_end_trans` is called to flush the pending events. But flush operation fails as the disk is full and the function simply returns `1' without taking any action to delete the pending event. This leaves the event to remain till the closure of connection. `delete pending' statement has been added to do the required clean up action. sql/log.cc: Added "delete pending" statement to clean pending event --- sql/log.cc | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'sql') diff --git a/sql/log.cc b/sql/log.cc index 57c14b24782..7e0e90e28c0 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -4313,10 +4313,16 @@ MYSQL_BIN_LOG::flush_and_set_pending_rows_event(THD *thd, /* Write pending event to log file or transaction cache */ + DBUG_EXECUTE_IF("simulate_disk_full_at_flush_pending", + {DBUG_SET("+d,simulate_file_write_error");}); if (pending->write(file)) { pthread_mutex_unlock(&LOCK_log); set_write_error(thd); + delete pending; + trx_data->set_pending(NULL); + DBUG_EXECUTE_IF("simulate_disk_full_at_flush_pending", + {DBUG_SET("-d,simulate_file_write_error");}); DBUG_RETURN(1); } -- cgit v1.2.1 From 9d007e075d6b933df9dbd1a42d52ab00ee91827c Mon Sep 17 00:00:00 2001 From: Harin Vadodaria Date: Mon, 17 Sep 2012 17:02:17 +0530 Subject: Bug#11753779: MAX_CONNECT_ERRORS WORKS ONLY WHEN 1ST INC_HOST_ERRORS() IS CALLED. Issue : Sequence of calling inc_host_errors() and reset_host_errors() required some changes in order to maintain correct connection error count. Solution : Call to reset_host_errors() is shifted to a location after which no calls to inc_host_errors() are made. --- sql/hostname.cc | 9 ++++++++ sql/sql_connect.cc | 68 +++++++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 69 insertions(+), 8 deletions(-) (limited to 'sql') diff --git a/sql/hostname.cc b/sql/hostname.cc index 9796755e9fb..38316a8ee19 100644 --- a/sql/hostname.cc +++ b/sql/hostname.cc @@ -214,6 +214,15 @@ char * ip_to_hostname(struct in_addr *in, uint *errors) } my_gethostbyname_r_free(); #else + + DBUG_EXECUTE_IF("addr_fake_ipv4", + { + const char* fake_host= "santa.claus.ipv4.example.com"; + name=my_strdup(fake_host, MYF(0)); + add_hostname(in,name); + DBUG_RETURN(name); + };); + VOID(pthread_mutex_lock(&LOCK_hostname)); if (!(hp=gethostbyaddr((char*) in,sizeof(*in), AF_INET))) { diff --git a/sql/sql_connect.cc b/sql/sql_connect.cc index 21e2701d06c..e7aa48c94f5 100644 --- a/sql/sql_connect.cc +++ b/sql/sql_connect.cc @@ -336,6 +336,7 @@ check_user(THD *thd, enum enum_server_command command, USER_RESOURCES ur; int res= acl_getroot(thd, &ur, passwd, passwd_len); + DBUG_EXECUTE_IF("password_format_mismatch",{res= -1;};); #ifndef EMBEDDED_LIBRARY if (res == -1) { @@ -346,6 +347,12 @@ check_user(THD *thd, enum enum_server_command command, in old format. */ NET *net= &thd->net; + DBUG_EXECUTE_IF("password_format_mismatch", + { + inc_host_errors(&thd->remote.sin_addr); + my_error(ER_HANDSHAKE_ERROR, MYF(0)); + DBUG_RETURN(1); + };); if (opt_secure_auth_local) { my_error(ER_SERVER_IS_IN_SECURE_AUTH_MODE, MYF(0), @@ -816,6 +823,8 @@ static int check_connection(THD *thd) size_t passwd_len; char *user; size_t user_len; + uint charset_code= 0; + size_t bytes_remaining_in_packet= 0; DBUG_PRINT("info", ("New connection received on %s", vio_description(net->vio))); @@ -832,6 +841,19 @@ static int check_connection(THD *thd) my_error(ER_BAD_HOST_ERROR, MYF(0)); return 1; } + /* BEGIN : DEBUG */ + DBUG_EXECUTE_IF("addr_fake_ipv4", + { + struct sockaddr *sa= (sockaddr *) &net->vio->remote; + sa->sa_family= AF_INET; + struct in_addr *ip4= &((struct sockaddr_in *)sa)->sin_addr; + /* See RFC 5737, 192.0.2.0/23 is reserved */ + const char* fake= "192.0.2.4"; + ip4->s_addr= inet_addr(fake); + strcpy(ip, fake); + };); + /* END : DEBUG */ + if (!(thd->main_security_ctx.ip= my_strdup(ip,MYF(MY_WME)))) return 1; /* The error is set by my_strdup(). */ thd->main_security_ctx.host_or_ip= thd->main_security_ctx.ip; @@ -927,32 +949,31 @@ static int check_connection(THD *thd) (uchar*) buff, (size_t) (end-buff)) || (pkt_len= my_net_read(net)) == packet_error) { - inc_host_errors(&thd->remote.sin_addr); - my_error(ER_HANDSHAKE_ERROR, MYF(0)); - return 1; + goto error; } } #ifdef _CUSTOMCONFIG_ #include "_cust_sql_parse.h" #endif - if (connect_errors) - reset_host_errors(&thd->remote.sin_addr); if (thd->packet.alloc(thd->variables.net_buffer_length)) return 1; /* The error is set by alloc(). */ - uint charset_code= 0; end= (char *)net->read_pos; /* In order to safely scan a head for '\0' string terminators we must keep track of how many bytes remain in the allocated buffer or we might read past the end of the buffer. */ - size_t bytes_remaining_in_packet= pkt_len; + bytes_remaining_in_packet= pkt_len; /* Peek ahead on the client capability packet and determine which version of the protocol should be used. */ + DBUG_EXECUTE_IF("host_error_packet_length", + { + bytes_remaining_in_packet= 0; + };); if (bytes_remaining_in_packet < 2) goto error; @@ -1011,6 +1032,10 @@ static int check_connection(THD *thd) skip_to_ssl: + DBUG_EXECUTE_IF("host_error_charset", + { + goto error; + };); DBUG_PRINT("info", ("client_character_set: %u", charset_code)); if (thd_init_client_charset(thd, charset_code)) goto error; @@ -1079,6 +1104,10 @@ skip_to_ssl: bytes_remaining_in_packet -= AUTH_PACKET_HEADER_SIZE_PROTO_40; } + DBUG_EXECUTE_IF("host_error_SSL_layering", + { + packet_has_required_size= 0; + };); if (!packet_has_required_size) goto error; } @@ -1104,6 +1133,11 @@ skip_to_ssl: get_string= get_40_protocol_string; user= get_string(&end, &bytes_remaining_in_packet, &user_len); + DBUG_EXECUTE_IF("host_error_user", + { + user= NULL; + };); + if (user == NULL) goto error; @@ -1131,6 +1165,11 @@ skip_to_ssl: passwd= get_string(&end, &bytes_remaining_in_packet, &passwd_len); } + DBUG_EXECUTE_IF("host_error_password", + { + passwd= NULL; + };); + if (passwd == NULL) goto error; @@ -1191,7 +1230,20 @@ skip_to_ssl: if (!(thd->main_security_ctx.user= my_strdup(user, MYF(MY_WME)))) return 1; /* The error is set by my_strdup(). */ - return check_user(thd, COM_CONNECT, passwd, passwd_len, db, TRUE); + + if (!check_user(thd, COM_CONNECT, passwd, passwd_len, db, TRUE)) + { + /* + Call to reset_host_errors() should be made only when all sanity checks + are done and connection is going to be a successful. + */ + reset_host_errors(&thd->remote.sin_addr); + return 0; + } + else + { + return 1; + } error: inc_host_errors(&thd->remote.sin_addr); -- cgit v1.2.1 From 5530c5e38dbefac8e5d2c333c0f35ed9f73946a4 Mon Sep 17 00:00:00 2001 From: Rohit Kalhans Date: Sat, 22 Sep 2012 17:50:51 +0530 Subject: BUG#14548159: NUMEROUS CASES OF INCORRECT IDENTIFIER QUOTING IN REPLICATION Problem: Misquoting or unquoted identifiers may lead to incorrect statements to be logged to the binary log. Fix: we use specialized functions to append quoted identifiers in the statements generated by the server. --- sql/ha_ndbcluster_binlog.cc | 31 +++++-- sql/log.cc | 35 +++++--- sql/log_event.cc | 209 ++++++++++++++++++++++++++++++++++---------- sql/log_event.h | 16 ++++ sql/sql_base.cc | 31 +++---- sql/sql_db.cc | 35 +++++--- sql/sql_insert.cc | 19 ++-- sql/sql_load.cc | 28 +++--- sql/sql_show.cc | 3 +- sql/sql_show.h | 1 + sql/sql_table.cc | 22 ++--- 11 files changed, 300 insertions(+), 130 deletions(-) (limited to 'sql') diff --git a/sql/ha_ndbcluster_binlog.cc b/sql/ha_ndbcluster_binlog.cc index fe802ce0e2d..70e52306e14 100644 --- a/sql/ha_ndbcluster_binlog.cc +++ b/sql/ha_ndbcluster_binlog.cc @@ -22,6 +22,7 @@ #include "rpl_injector.h" #include "rpl_filter.h" #include "slave.h" +#include "log_event.h" #include "ha_ndbcluster_binlog.h" #include "NdbDictionary.hpp" #include "ndb_cluster_connection.hpp" @@ -1269,6 +1270,11 @@ int ndbcluster_log_schema_op(THD *thd, NDB_SHARE *share, } char tmp_buf2[FN_REFLEN]; + char quoted_table1[2 + 2 * FN_REFLEN + 1]; + char quoted_db1[2 + 2 * FN_REFLEN + 1]; + char quoted_db2[2 + 2 * FN_REFLEN + 1]; + char quoted_table2[2 + 2 * FN_REFLEN + 1]; + int id_length= 0; const char *type_str; switch (type) { @@ -1278,16 +1284,31 @@ int ndbcluster_log_schema_op(THD *thd, NDB_SHARE *share, DBUG_RETURN(0); /* redo the drop table query as is may contain several tables */ query= tmp_buf2; - query_length= (uint) (strxmov(tmp_buf2, "drop table `", - table_name, "`", NullS) - tmp_buf2); + id_length= my_strmov_quoted_identifier (thd, (char *) quoted_table1, + table_name, 0); + quoted_table1[id_length]= '\0'; + query_length= (uint) (strxmov(tmp_buf2, "drop table ", + quoted_table1, NullS) - tmp_buf2); type_str= "drop table"; break; case SOT_RENAME_TABLE: /* redo the rename table query as is may contain several tables */ query= tmp_buf2; - query_length= (uint) (strxmov(tmp_buf2, "rename table `", - db, ".", table_name, "` to `", - new_db, ".", new_table_name, "`", NullS) - tmp_buf2); + id_length= my_strmov_quoted_identifier (thd, (char *) quoted_db1, + db, 0); + quoted_db1[id_length]= '\0'; + id_length= my_strmov_quoted_identifier (thd, (char *) quoted_table1, + table_name, 0); + quoted_table1[id_length]= '\0'; + id_length= my_strmov_quoted_identifier (thd, (char *) quoted_db2, + new_db, 0); + quoted_db2[id_length]= '\0'; + id_length= my_strmov_quoted_identifier (thd, (char *) quoted_table2, + new_table_name, 0); + quoted_table2[id_length]= '\0'; + query_length= (uint) (strxmov(tmp_buf2, "rename table ", + quoted_db1, ".", quoted_table_1, " to ", + quoted_db2, ".", quoted_table2, NullS) - tmp_buf2); type_str= "rename table"; break; case SOT_CREATE_TABLE: diff --git a/sql/log.cc b/sql/log.cc index 7e0e90e28c0..93dd70b33c5 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -29,6 +29,7 @@ #include "rpl_filter.h" #include "rpl_rli.h" +#include "sql_show.h" #include #include #include // For test_if_number @@ -1708,17 +1709,24 @@ static int binlog_savepoint_set(handlerton *hton, THD *thd, void *sv) DBUG_ENTER("binlog_savepoint_set"); binlog_trans_log_savepos(thd, (my_off_t*) sv); + + // buffer to store quoted identifier + char* buffer= (char *)my_malloc(sizeof("SAVEPOINT ")+ 1 + NAME_LEN * 2 + 2, + MYF(0)); + String log_query(buffer, sizeof(buffer), system_charset_info); + log_query.length(0); + /* Write it to the binary log */ - String log_query; - if (log_query.append(STRING_WITH_LEN("SAVEPOINT ")) || - log_query.append("`") || - log_query.append(thd->lex->ident.str, thd->lex->ident.length) || - log_query.append("`")) + if (log_query.append(STRING_WITH_LEN("SAVEPOINT "))) DBUG_RETURN(1); + else + append_identifier(thd, &log_query, thd->lex->ident.str, + thd->lex->ident.length); int errcode= query_error_code(thd, thd->killed == THD::NOT_KILLED); Query_log_event qinfo(thd, log_query.c_ptr_safe(), log_query.length(), TRUE, TRUE, errcode); + my_free(buffer, MYF(MY_WME)); DBUG_RETURN(mysql_bin_log.write(&qinfo)); } @@ -1731,18 +1739,23 @@ static int binlog_savepoint_rollback(handlerton *hton, THD *thd, void *sv) non-transactional table. Otherwise, truncate the binlog cache starting from the SAVEPOINT command. */ - if (unlikely(trans_has_updated_non_trans_table(thd) || + if (unlikely(trans_has_updated_non_trans_table(thd) || (thd->options & OPTION_KEEP_LOG))) { - String log_query; - if (log_query.append(STRING_WITH_LEN("ROLLBACK TO ")) || - log_query.append("`") || - log_query.append(thd->lex->ident.str, thd->lex->ident.length) || - log_query.append("`")) + // buffer to store rollback query with quoted identifier + char* buffer= (char *)my_malloc(12 + 1 + NAME_LEN * 2 + 2, MYF(0)); + String log_query(buffer, sizeof(buffer), system_charset_info); + log_query.length(0); + + if (log_query.append(STRING_WITH_LEN("ROLLBACK TO "))) DBUG_RETURN(1); + else + append_identifier(thd, &log_query, thd->lex->ident.str, + thd->lex->ident.length); int errcode= query_error_code(thd, thd->killed == THD::NOT_KILLED); Query_log_event qinfo(thd, log_query.c_ptr_safe(), log_query.length(), TRUE, TRUE, errcode); + my_free(buffer, MYF(MY_WME)); DBUG_RETURN(mysql_bin_log.write(&qinfo)); } binlog_trans_log_truncate(thd, *(my_off_t*)sv); diff --git a/sql/log_event.cc b/sql/log_event.cc index 2f27efa8b4e..829ee06d20e 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -34,6 +34,7 @@ #include "rpl_utility.h" #include "rpl_record.h" #include +#include "sql_show.h" // append_identifier #endif /* MYSQL_CLIENT */ @@ -1946,6 +1947,10 @@ Rows_log_event::print_verbose_one_row(IO_CACHE *file, table_def *td, void Rows_log_event::print_verbose(IO_CACHE *file, PRINT_EVENT_INFO *print_event_info) { + // Quoted length of the identifier can be twice the original length + char quoted_db[1 + NAME_LEN * 2 + 2]; + char quoted_table[1 + NAME_LEN * 2 + 2]; + int quoted_db_len, quoted_table_len; Table_map_log_event *map; table_def *td; const char *sql_command, *sql_clause1, *sql_clause2; @@ -1982,9 +1987,23 @@ void Rows_log_event::print_verbose(IO_CACHE *file, for (const uchar *value= m_rows_buf; value < m_rows_end; ) { size_t length; +#ifdef MYSQL_SERVER + quoted_db_len= my_strmov_quoted_identifier(this->thd, (char *) quoted_db, + map->get_db_name(), 0); + quoted_table_len= my_strmov_quoted_identifier(this->thd, + (char *) quoted_table, + map->get_table_name(), 0); +#else + quoted_db_len= my_strmov_quoted_identifier((char *) quoted_db, + map->get_db_name()); + quoted_table_len= my_strmov_quoted_identifier((char *) quoted_table, + map->get_table_name()); +#endif + quoted_db[quoted_db_len]= '\0'; + quoted_table[quoted_table_len]= '\0'; my_b_printf(file, "### %s %s.%s\n", sql_command, - map->get_db_name(), map->get_table_name()); + quoted_db, quoted_table); /* Print the first image */ if (!(length= print_verbose_one_row(file, td, print_event_info, &m_cols, value, @@ -2143,24 +2162,20 @@ Log_event::continue_group(Relay_log_info *rli) void Query_log_event::pack_info(Protocol *protocol) { // TODO: show the catalog ?? - char *buf, *pos; - if (!(buf= (char*) my_malloc(9 + db_len + q_len, MYF(MY_WME)))) - return; - pos= buf; + String temp_buf; + // Add use `DB` to the string if required if (!(flags & LOG_EVENT_SUPPRESS_USE_F) && db && db_len) { - pos= strmov(buf, "use `"); - memcpy(pos, db, db_len); - pos= strmov(pos+db_len, "`; "); + temp_buf.append("use "); + append_identifier(this->thd, &temp_buf, db, db_len); + temp_buf.append("; "); } + // Add the query to the string if (query && q_len) - { - memcpy(pos, query, q_len); - pos+= q_len; - } - protocol->store(buf, pos-buf, &my_charset_bin); - my_free(buf, MYF(MY_ALLOW_ZERO_PTR)); + temp_buf.append(query); + // persist the buffer in protocol + protocol->store(temp_buf.ptr(), temp_buf.length(), &my_charset_bin); } #endif @@ -2932,6 +2947,8 @@ void Query_log_event::print_query_header(IO_CACHE* file, { // TODO: print the catalog ?? char buff[40],*end; // Enough for SET TIMESTAMP + char quoted_id[1+ 2*FN_REFLEN+ 2]; + int quoted_len= 0; bool different_db= 1; uint32 tmp; @@ -2950,11 +2967,17 @@ void Query_log_event::print_query_header(IO_CACHE* file, } else if (db) { +#ifdef MYSQL_SERVER + quoted_len= my_strmov_quoted_identifier(this->thd, (char*)quoted_id, db, 0); +#else + quoted_len= my_strmov_quoted_identifier((char*)quoted_id, db); +#endif + quoted_id[quoted_len]= '\0'; different_db= memcmp(print_event_info->db, db, db_len + 1); if (different_db) memcpy(print_event_info->db, db, db_len + 1); if (db[0] && different_db) - my_b_printf(file, "use %s%s\n", db, print_event_info->delimiter); + my_b_printf(file, "use %s%s\n", quoted_id, print_event_info->delimiter); } end=int10_to_str((long) when, strmov(buff,"SET TIMESTAMP="),10); @@ -4216,7 +4239,8 @@ void Format_description_log_event::calc_server_version_split() uint Load_log_event::get_query_buffer_length() { return - 5 + db_len + 3 + // "use DB; " + //the DB name may double if we escape the quote character + 5 + 2*db_len + 3 + 18 + fname_len + 2 + // "LOAD DATA INFILE 'file''" 11 + // "CONCURRENT " 7 + // LOCAL @@ -4235,13 +4259,21 @@ uint Load_log_event::get_query_buffer_length() void Load_log_event::print_query(bool need_db, const char *cs, char *buf, char **end, char **fn_start, char **fn_end) { + char quoted_id[1 + NAME_LEN * 2 + 2];//quoted length + int quoted_id_len= 0; char *pos= buf; if (need_db && db && db_len) { - pos= strmov(pos, "use `"); - memcpy(pos, db, db_len); - pos= strmov(pos+db_len, "`; "); + pos= strmov(pos, "use "); +#ifdef MYSQL_SERVER + quoted_id_len= my_strmov_quoted_identifier(this->thd, (char *) quoted_id, + db, 0); +#else + quoted_id_len= my_strmov_quoted_identifier((char *) quoted_id, db); +#endif + pos+= quoted_id_len; + pos= strmov(pos, "; "); } pos= strmov(pos, "LOAD DATA "); @@ -4268,17 +4300,15 @@ void Load_log_event::print_query(bool need_db, const char *cs, char *buf, if (fn_end) *fn_end= pos; - pos= strmov(pos ," TABLE `"); + pos= strmov(pos ," TABLE "); memcpy(pos, table_name, table_name_len); pos+= table_name_len; if (cs != NULL) { - pos= strmov(pos ,"` CHARACTER SET "); + pos= strmov(pos ," CHARACTER SET "); pos= strmov(pos , cs); } - else - pos= strmov(pos, "`"); /* We have to create all optional fields as the default is not empty */ pos= strmov(pos, " FIELDS TERMINATED BY "); @@ -4318,9 +4348,9 @@ void Load_log_event::print_query(bool need_db, const char *cs, char *buf, *pos++= ' '; *pos++= ','; } - memcpy(pos, field, field_lens[i]); - pos+= field_lens[i]; - field+= field_lens[i] + 1; + quoted_id_len= my_strmov_quoted_identifier(this->thd, quoted_id, field, + 0); + memcpy(pos, quoted_id, quoted_id_len); } *pos++= ')'; } @@ -4560,6 +4590,8 @@ void Load_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info) void Load_log_event::print(FILE* file_arg, PRINT_EVENT_INFO* print_event_info, bool commented) { + size_t id_len= 0; + char temp_buf[1 + 2*FN_REFLEN + 2]; Write_on_release_cache cache(&print_event_info->head_cache, file_arg); DBUG_ENTER("Load_log_event::print"); @@ -4585,10 +4617,16 @@ void Load_log_event::print(FILE* file_arg, PRINT_EVENT_INFO* print_event_info, } if (db && db[0] && different_db) - my_b_printf(&cache, "%suse %s%s\n", - commented ? "# " : "", - db, print_event_info->delimiter); - + { +#ifdef MYSQL_SERVER + id_len= my_strmov_quoted_identifier(this->thd, temp_buf, db, 0); +#else + id_len= my_strmov_quoted_identifier(temp_buf, db); +#endif + temp_buf[id_len]= '\0'; + my_b_printf(&cache, "%suse %s%s\n", + commented ? "# " : "", temp_buf, print_event_info->delimiter); + } if (flags & LOG_EVENT_THREAD_SPECIFIC_F) my_b_printf(&cache,"%sSET @@session.pseudo_thread_id=%lu%s\n", commented ? "# " : "", (ulong)thread_id, @@ -4603,8 +4641,14 @@ void Load_log_event::print(FILE* file_arg, PRINT_EVENT_INFO* print_event_info, my_b_printf(&cache,"REPLACE "); else if (sql_ex.opt_flags & IGNORE_FLAG) my_b_printf(&cache,"IGNORE "); - - my_b_printf(&cache, "INTO TABLE `%s`", table_name); + +#ifdef MYSQL_SERVER + id_len= my_strmov_quoted_identifier(this->thd, temp_buf, table_name, 0); +#else + id_len= my_strmov_quoted_identifier(temp_buf, table_name); +#endif + temp_buf[id_len]= '\0'; + my_b_printf(&cache, "INTO TABLE %s", temp_buf); my_b_printf(&cache, " FIELDS TERMINATED BY "); pretty_print_str(&cache, sql_ex.field_term, sql_ex.field_term_len); @@ -4637,7 +4681,9 @@ void Load_log_event::print(FILE* file_arg, PRINT_EVENT_INFO* print_event_info, { if (i) my_b_printf(&cache, ","); - my_b_printf(&cache, "%s", field); + id_len= my_strmov_quoted_identifier((char *) temp_buf, field); + temp_buf[id_len]= '\0'; + my_b_printf(&cache, "%s", temp_buf); field += field_lens[i] + 1; } @@ -5560,7 +5606,10 @@ Xid_log_event::do_shall_skip(Relay_log_info *rli) void User_var_log_event::pack_info(Protocol* protocol) { char *buf= 0; - uint val_offset= 4 + name_len; + char quoted_id[1 + FN_REFLEN * 2 + 2];// quoted identifier + int id_len= my_strmov_quoted_identifier(this->thd, quoted_id, name, 0); + quoted_id[id_len]= '\0'; + uint val_offset= 2 + id_len; uint event_len= val_offset; if (is_null) @@ -5626,10 +5675,8 @@ void User_var_log_event::pack_info(Protocol* protocol) } } buf[0]= '@'; - buf[1]= '`'; - memcpy(buf+2, name, name_len); - buf[2+name_len]= '`'; - buf[3+name_len]= '='; + memcpy(buf + 1, quoted_id, id_len); + buf[1 + id_len]= '='; protocol->store(buf, event_len, &my_charset_bin); my_free(buf, MYF(0)); } @@ -5740,6 +5787,8 @@ bool User_var_log_event::write(IO_CACHE* file) #ifdef MYSQL_CLIENT void User_var_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info) { + char quoted_id[1 + NAME_LEN * 2 + 2];// quoted length of the identifier + int quoted_len= 0; Write_on_release_cache cache(&print_event_info->head_cache, file, Write_on_release_cache::FLUSH_F); @@ -5749,9 +5798,11 @@ void User_var_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info) my_b_printf(&cache, "\tUser_var\n"); } - my_b_printf(&cache, "SET @`"); - my_b_write(&cache, (uchar*) name, (uint) (name_len)); - my_b_printf(&cache, "`"); + my_b_printf(&cache, "SET @"); + quoted_len= my_strmov_quoted_identifier((char *) quoted_id, + (const char *) name); + quoted_id[quoted_len]= '\0'; + my_b_write(&cache, (uchar*) quoted_id, (uint) quoted_len); if (is_null) { @@ -7043,14 +7094,23 @@ void Execute_load_query_log_event::print(FILE* file, void Execute_load_query_log_event::pack_info(Protocol *protocol) { char *buf, *pos; - if (!(buf= (char*) my_malloc(9 + db_len + q_len + 10 + 21, MYF(MY_WME)))) + if (!(buf= (char*) my_malloc(9 + (db_len * 2) + 2 + q_len + 10 + 21, + MYF(MY_WME)))) return; pos= buf; if (db && db_len) { - pos= strmov(buf, "use `"); - memcpy(pos, db, db_len); - pos= strmov(pos+db_len, "`; "); + /* + Statically allocates room to store '\0' and an identifier + that may have NAME_LEN * 2 due to quoting and there are + two quoting characters that wrap them. + */ + char quoted_db[1 + NAME_LEN * 2 + 2];// quoted length of the identifier + size_t size= 0; + size= my_strmov_quoted_identifier(this->thd, quoted_db, db, 0); + pos= strmov(buf, "use "); + memcpy(pos, quoted_db, size); + pos= strmov(pos + size, "; "); } if (query && q_len) { @@ -9907,3 +9967,62 @@ st_print_event_info::st_print_event_info() open_cached_file(&body_cache, NULL, NULL, 0, flags); } #endif + +#ifdef MYSQL_SERVER +/* + This is a utility function that adds a quoted identifier into the a buffer. + This also escapes any existance of the quote string inside the identifier. + + SYNOPSIS + my_strmov_quoted_identifier + thd thread handler + buffer target buffer + identifier the identifier to be quoted + length length of the identifier +*/ +size_t my_strmov_quoted_identifier(THD* thd, char *buffer, + const char* identifier, + uint length) +{ + int q= thd ? get_quote_char_for_identifier(thd, identifier, length) : '`'; + return my_strmov_quoted_identifier_helper(q, buffer, identifier, length); +} +#else +size_t my_strmov_quoted_identifier(char *buffer, const char* identifier) +{ + int q= '`'; + return my_strmov_quoted_identifier_helper(q, buffer, identifier, 0); +} + +#endif + +size_t my_strmov_quoted_identifier_helper(int q, char *buffer, + const char* identifier, + uint length) +{ + size_t written= 0; + char quote_char; + uint id_length= (length) ? length : strlen(identifier); + + if (q == EOF) + { + (void *) strncpy(buffer, identifier, id_length); + return id_length; + } + quote_char= (char) q; + *buffer++= quote_char; + written++; + while (id_length--) + { + if (*identifier == quote_char) + { + *buffer++= quote_char; + written++; + } + *buffer++= *identifier++; + written++; + } + *buffer++= quote_char; + return ++written; +} + diff --git a/sql/log_event.h b/sql/log_event.h index 6b411a90382..ba6b9b876aa 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -3987,6 +3987,22 @@ static inline bool copy_event_cache_to_file_and_reinit(IO_CACHE *cache, bool slave_execute_deferred_events(THD *thd); #endif +#ifdef MYSQL_SERVER +/** + This is an utility function that adds a quoted identifier into the a buffer. + This also escapes any existance of the quote string inside the identifier. + */ +size_t my_strmov_quoted_identifier(THD *thd, char *buffer, + const char* identifier, + uint length); +#else +size_t my_strmov_quoted_identifier(char *buffer, const char* identifier); +#endif +size_t my_strmov_quoted_identifier_helper(int q, char *buffer, + const char* identifier, + uint length); + + /** @} (end of group Replication) */ diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 2ce5ec81917..c7513f45983 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -4144,24 +4144,19 @@ retry: entry->file->implicit_emptied= 0; if (mysql_bin_log.is_open()) { - char *query, *end; - uint query_buf_size= 20 + share->db.length + share->table_name.length +1; - if ((query= (char*) my_malloc(query_buf_size,MYF(MY_WME)))) - { - /* this DELETE FROM is needed even with row-based binlogging */ - end = strxmov(strmov(query, "DELETE FROM `"), - share->db.str,"`.`",share->table_name.str,"`", NullS); - int errcode= query_error_code(thd, TRUE); - if (thd->binlog_query(THD::STMT_QUERY_TYPE, - query, (ulong)(end-query), - FALSE, FALSE, errcode)) - { - my_free(query, MYF(0)); - goto err; - } - my_free(query, MYF(0)); - } - else + bool error= false; + String temp_buf; + error= temp_buf.append("DELETE FROM "); + append_identifier(thd, &temp_buf, share->db.str, strlen(share->db.str)); + error= temp_buf.append("."); + append_identifier(thd, &temp_buf, share->table_name.str, + strlen(share->table_name.str)); + int errcode= query_error_code(thd, TRUE); + if (thd->binlog_query(THD::STMT_QUERY_TYPE, + temp_buf.ptr(), temp_buf.length(), + FALSE, FALSE, errcode)) + goto err; + if(error) { /* As replication is maybe going to be corrupted, we need to warn the diff --git a/sql/sql_db.cc b/sql/sql_db.cc index 39c33da23ef..fe3aed5c8f0 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -25,6 +25,7 @@ #include #include #include "log.h" +#include "log_event.h" #ifdef __WIN__ #include #endif @@ -718,12 +719,17 @@ not_silent: { char *query; uint query_length; + char db_name_quoted[2 * FN_REFLEN + sizeof("create database ") + 2]; + int id_len= 0; if (!thd->query()) // Only in replication { - query= tmp_query; - query_length= (uint) (strxmov(tmp_query,"create database `", - db, "`", NullS) - tmp_query); + id_len= my_strmov_quoted_identifier(thd, (char *) db_name_quoted, db, + 0); + db_name_quoted[id_len]= '\0'; + query= tmp_query; + query_length= (uint) (strxmov(tmp_query,"create database ", + db_name_quoted, NullS) - tmp_query); } else { @@ -889,7 +895,7 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent) { long deleted=0; int error= 0; - char path[FN_REFLEN+16]; + char path[2 * FN_REFLEN + 16]; MY_DIR *dirp; uint length; TABLE_LIST* dropped_tables= 0; @@ -989,11 +995,17 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent) { const char *query; ulong query_length; + // quoted db name + wraping quote + char buffer_temp [2 * FN_REFLEN + 2]; + int id_len= 0; + if (!thd->query()) { /* The client used the old obsolete mysql_drop_db() call */ query= path; - query_length= (uint) (strxmov(path, "drop database `", db, "`", + id_len= my_strmov_quoted_identifier(thd, buffer_temp, db, strlen(db)); + buffer_temp[id_len] ='\0'; + query_length= (uint) (strxmov(path, "DROP DATABASE ", buffer_temp, "", NullS) - path); } else @@ -1029,12 +1041,13 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent) else if (mysql_bin_log.is_open()) { char *query, *query_pos, *query_end, *query_data_start; + char temp_identifier[ 2 * FN_REFLEN + 2]; TABLE_LIST *tbl; - uint db_len; + uint db_len, id_length=0; if (!(query= (char*) thd->alloc(MAX_DROP_TABLE_Q_LEN))) goto exit; /* not much else we can do */ - query_pos= query_data_start= strmov(query,"drop table "); + query_pos= query_data_start= strmov(query,"DROP TABLE "); query_end= query + MAX_DROP_TABLE_Q_LEN; db_len= strlen(db); @@ -1054,10 +1067,10 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent) } query_pos= query_data_start; } - - *query_pos++ = '`'; - query_pos= strmov(query_pos,tbl->table_name); - *query_pos++ = '`'; + id_length= my_strmov_quoted_identifier(thd, (char *)temp_identifier, + tbl->table_name, 0); + temp_identifier[id_length]= '\0'; + query_pos= strmov(query_pos,(char *)&temp_identifier); *query_pos++ = ','; } diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 4cd456829ba..7e94e7e7df3 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -3426,16 +3426,16 @@ int select_create::write_to_binlog(bool is_trans, int errcode) if (thd->lex->create_select_in_comment) query.append(STRING_WITH_LEN("/*! ")); if (thd->lex->ignore) - query.append(STRING_WITH_LEN("INSERT IGNORE INTO `")); + query.append(STRING_WITH_LEN("INSERT IGNORE INTO ")); else if (thd->lex->duplicates == DUP_REPLACE) - query.append(STRING_WITH_LEN("REPLACE INTO `")); + query.append(STRING_WITH_LEN("REPLACE INTO ")); else - query.append(STRING_WITH_LEN("INSERT INTO `")); + query.append(STRING_WITH_LEN("INSERT INTO ")); - query.append(create_table->db, db_len); - query.append(STRING_WITH_LEN("`.`")); - query.append(create_info->alias, table_len); - query.append(STRING_WITH_LEN("` ")); + append_identifier(thd, &query, create_table->db, db_len); + query.append(STRING_WITH_LEN(".")); + append_identifier(thd, &query, create_info->alias, table_len ); + query.append(STRING_WITH_LEN(" ")); /* The insert items. @@ -3447,9 +3447,8 @@ int select_create::write_to_binlog(bool is_trans, int errcode) if (f != field) query.append(STRING_WITH_LEN(",")); - query.append(STRING_WITH_LEN("`")); - query.append((*f)->field_name, strlen((*f)->field_name)); - query.append(STRING_WITH_LEN("`")); + append_identifier(thd, &query, (*f)->field_name, + strlen((*f)->field_name)); } query.append(STRING_WITH_LEN(") ")); diff --git a/sql/sql_load.cc b/sql/sql_load.cc index 6a0e5fd9133..b736fa59c22 100644 --- a/sql/sql_load.cc +++ b/sql/sql_load.cc @@ -25,6 +25,7 @@ #include "sp_head.h" #include "sql_trigger.h" +#include "sql_show.h" class READ_INFO { File file; uchar *buffer, /* Buffer for read text */ @@ -619,23 +620,20 @@ static bool write_execute_load_query_log_event(THD *thd, sql_exchange* ex, const char *tbl= table_name_arg; const char *tdb= (thd->db != NULL ? thd->db : db_arg); String string_buf; - - if (!thd->db || strcmp(db_arg, thd->db)) + if (!thd->db || strcmp(db_arg, thd->db)) { /* - If used database differs from table's database, - prefix table name with database name so that it + If used database differs from table's database, + prefix table name with database name so that it becomes a FQ name. */ string_buf.set_charset(system_charset_info); - string_buf.append(db_arg); - string_buf.append("`"); + append_identifier(thd, &string_buf, db_arg, strlen(db_arg)); string_buf.append("."); - string_buf.append("`"); - string_buf.append(table_name_arg); - tbl= string_buf.c_ptr_safe(); } - + append_identifier(thd, &string_buf, table_name_arg, + strlen(table_name_arg)); + tbl= string_buf.c_ptr_safe(); Load_log_event lle(thd, ex, tdb, tbl, fv, duplicates, ignore, transactional_table); @@ -660,11 +658,7 @@ static bool write_execute_load_query_log_event(THD *thd, sql_exchange* ex, if (n++) pfields.append(", "); if (item->name) - { - pfields.append("`"); - pfields.append(item->name); - pfields.append("`"); - } + append_identifier(thd, &pfields, item->name, strlen(item->name)); else item->print(&pfields, QT_ORDINARY); } @@ -684,9 +678,7 @@ static bool write_execute_load_query_log_event(THD *thd, sql_exchange* ex, val= lv++; if (n++) pfields.append(", "); - pfields.append("`"); - pfields.append(item->name); - pfields.append("`"); + append_identifier(thd, &pfields, item->name, strlen(item->name)); pfields.append("="); val->print(&pfields, QT_ORDINARY); } diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 7847fe5b510..7859d7f61e3 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -1006,7 +1006,8 @@ append_identifier(THD *thd, String *packet, const char *name, uint length) { const char *name_end; char quote_char; - int q= get_quote_char_for_identifier(thd, name, length); + int q; + q= thd ? get_quote_char_for_identifier(thd, name, length) : '`'; if (q == EOF) { diff --git a/sql/sql_show.h b/sql/sql_show.h index fec73122e8b..3a6cd158cb4 100644 --- a/sql/sql_show.h +++ b/sql/sql_show.h @@ -40,5 +40,6 @@ int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet, int view_store_create_info(THD *thd, TABLE_LIST *table, String *buff); int copy_event_to_schema_table(THD *thd, TABLE *sch_table, TABLE *event_table); +int get_quote_char_for_identifier(THD *thd, const char *name, uint length); #endif /* SQL_SHOW_H */ diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 39eee62ee91..b8d57341e42 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -1944,6 +1944,7 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, for (table= tables; table; table= table->next_local) { char *db=table->db; + int db_len= table->db_length; handlerton *table_type; enum legacy_db_type frm_db_type= DB_TYPE_UNKNOWN; @@ -1966,14 +1967,14 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, built_tmp_query.append("DROP TEMPORARY TABLE IF EXISTS "); } - built_tmp_query.append("`"); if (thd->db == NULL || strcmp(db,thd->db) != 0) { - built_tmp_query.append(db); - built_tmp_query.append("`.`"); + append_identifier(thd, &built_tmp_query, db, db_len); + built_tmp_query.append("."); } - built_tmp_query.append(table->table_name); - built_tmp_query.append("`,"); + append_identifier(thd, &built_tmp_query, table->table_name, + strlen(table->table_name)); + built_tmp_query.append(","); } continue; @@ -1999,15 +2000,14 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, Don't write the database name if it is the current one (or if thd->db is NULL). */ - built_query.append("`"); if (thd->db == NULL || strcmp(db,thd->db) != 0) { - built_query.append(db); - built_query.append("`.`"); + append_identifier(thd, &built_query, db, db_len); + built_query.append("."); } - - built_query.append(table->table_name); - built_query.append("`,"); + append_identifier(thd, &built_query, table->table_name, + strlen(table->table_name)); + built_query.append(","); } if (!drop_temporary) -- cgit v1.2.1 From 7c671a7ead96dd9115a6ae77ebbf78d0dea43485 Mon Sep 17 00:00:00 2001 From: Rohit Kalhans Date: Sun, 23 Sep 2012 15:45:22 +0530 Subject: BUG#14548159: Followup patch to fix some issues on PB2 --- sql/ha_ndbcluster_binlog.cc | 2 +- sql/log_event.cc | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'sql') diff --git a/sql/ha_ndbcluster_binlog.cc b/sql/ha_ndbcluster_binlog.cc index 70e52306e14..6d2a6f06c99 100644 --- a/sql/ha_ndbcluster_binlog.cc +++ b/sql/ha_ndbcluster_binlog.cc @@ -1307,7 +1307,7 @@ int ndbcluster_log_schema_op(THD *thd, NDB_SHARE *share, new_table_name, 0); quoted_table2[id_length]= '\0'; query_length= (uint) (strxmov(tmp_buf2, "rename table ", - quoted_db1, ".", quoted_table_1, " to ", + quoted_db1, ".", quoted_table1, " to ", quoted_db2, ".", quoted_table2, NullS) - tmp_buf2); type_str= "rename table"; break; diff --git a/sql/log_event.cc b/sql/log_event.cc index 829ee06d20e..c3ba969cf1f 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -4259,7 +4259,7 @@ uint Load_log_event::get_query_buffer_length() void Load_log_event::print_query(bool need_db, const char *cs, char *buf, char **end, char **fn_start, char **fn_end) { - char quoted_id[1 + NAME_LEN * 2 + 2];//quoted length + char quoted_id[1 + NAME_LEN * 2 + 2];//quoted length int quoted_id_len= 0; char *pos= buf; @@ -4272,7 +4272,8 @@ void Load_log_event::print_query(bool need_db, const char *cs, char *buf, #else quoted_id_len= my_strmov_quoted_identifier((char *) quoted_id, db); #endif - pos+= quoted_id_len; + quoted_id[quoted_id_len]= '\0'; + pos= strmov(pos, quoted_id); pos= strmov(pos, "; "); } -- cgit v1.2.1 From 815aad69287d83a1e1e98cd7797d02ca96f74bc3 Mon Sep 17 00:00:00 2001 From: Raghav Kapoor Date: Tue, 25 Sep 2012 15:58:46 +0530 Subject: BUG#13864642: DROP/CREATE USER BEHAVING ODDLY BACKGROUND: In certain situations DROP USER fails to remove all privileges belonging to user being dropped from in-memory structures. Current workaround is to do DROP USER twice in scenario below OR doing FLUSH PRIVILEGES after doing DROP USER. ANALYSIS: In MySQL, When we grant some stored routines privileges to a user they are stored in their respective hash. When doing DROP USER all the stored routine privilege entries associated with that user has to be deleted from its respective hash. The root cause for this bug is some entries from the hash are not getting deleted. The problem is that code that deletes entries from the hash tries to do so while iterating over it, without taking enough measures to address the fact that such deletion can reshuffle elements in the hash. If the user/administrator creates the same user again he is thrown an error 'Error 1396 ER_CANNOT_USER' from MySQL. This prompts the user to either do FLUSH PRIVILEGES or do DROP USER again. This behaviour is not desirable as it is a workaround and does not solves the problem mentioned above. FIX: This bug is fixed by introducing a dynamic array to store the pointersto all stored routine privilege objects that either have to be deleted or updated. This is done in 3 steps. Step 1: Fetching the element from the hash and checking whether it is to be deleted or updated. Step 2: Storing the pointer to that privilege object in dynamic array. Step 3: Traversing the dynamic array to perform the appropriate action either delete or update. This is a much cleaner way to delete or update the privilege entries associated with some user and solves the problem mentioned above. Also the code has been refactored a bit by introducing an enum instead of hard coded numbers used for respective dynamic arrays and hashes in handle_grant_struct() function. --- sql/sql_acl.cc | 235 +++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 146 insertions(+), 89 deletions(-) (limited to 'sql') diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 6d5d34d0602..e07f668b1cf 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -195,7 +195,17 @@ static bool compare_hostname(const acl_host_and_ip *host,const char *hostname, static my_bool acl_load(THD *thd, TABLE_LIST *tables); static my_bool grant_load(THD *thd, TABLE_LIST *tables); static inline void get_grantor(THD *thd, char* grantor); - +/* + Enumeration of various ACL's and Hashes used in handle_grant_struct() +*/ +enum enum_acl_lists +{ + USER_ACL= 0, + DB_ACL, + COLUMN_PRIVILEGES_HASH, + PROC_PRIVILEGES_HASH, + FUNC_PRIVILEGES_HASH +}; /* Convert scrambled password to binary form, according to scramble type, Binary form is stored in user.salt. @@ -5390,19 +5400,19 @@ static int handle_grant_table(TABLE_LIST *tables, uint table_no, bool drop, Delete from grant structure if drop is true. Update in grant structure if drop is false and user_to is not NULL. Search in grant structure if drop is false and user_to is NULL. - Structures are numbered as follows: - 0 acl_users - 1 acl_dbs - 2 column_priv_hash - 3 proc_priv_hash - 4 func_priv_hash + Structures are enumerated as follows: + 0 ACL_USER + 1 ACL_DB + 2 COLUMN_PRIVILEGES_HASH + 3 PROC_PRIVILEGES_HASH + 4 FUNC_PRIVILEGES_HASH @retval > 0 At least one element matched. @retval 0 OK, but no element matched. - @retval -1 Wrong arguments to function. + @retval -1 Wrong arguments to function or Out of Memory */ -static int handle_grant_struct(uint struct_no, bool drop, +static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop, LEX_USER *user_from, LEX_USER *user_to) { int result= 0; @@ -5413,6 +5423,11 @@ static int handle_grant_struct(uint struct_no, bool drop, ACL_USER *acl_user= NULL; ACL_DB *acl_db= NULL; GRANT_NAME *grant_name= NULL; + /* + Dynamic array acl_grant_name used to store pointers to all + GRANT_NAME objects + */ + Dynamic_array acl_grant_name; HASH *grant_name_hash= NULL; DBUG_ENTER("handle_grant_struct"); DBUG_PRINT("info",("scan struct: %u search: '%s'@'%s'", @@ -5425,21 +5440,21 @@ static int handle_grant_struct(uint struct_no, bool drop, /* Get the number of elements in the in-memory structure. */ switch (struct_no) { - case 0: + case USER_ACL: elements= acl_users.elements; break; - case 1: + case DB_ACL: elements= acl_dbs.elements; break; - case 2: + case COLUMN_PRIVILEGES_HASH: elements= column_priv_hash.records; grant_name_hash= &column_priv_hash; break; - case 3: + case PROC_PRIVILEGES_HASH: elements= proc_priv_hash.records; grant_name_hash= &proc_priv_hash; break; - case 4: + case FUNC_PRIVILEGES_HASH: elements= func_priv_hash.records; grant_name_hash= &func_priv_hash; break; @@ -5458,21 +5473,21 @@ static int handle_grant_struct(uint struct_no, bool drop, Get a pointer to the element. */ switch (struct_no) { - case 0: + case USER_ACL: acl_user= dynamic_element(&acl_users, idx, ACL_USER*); user= acl_user->user; host= acl_user->host.hostname; break; - case 1: + case DB_ACL: acl_db= dynamic_element(&acl_dbs, idx, ACL_DB*); user= acl_db->user; host= acl_db->host.hostname; break; - case 2: - case 3: - case 4: + case COLUMN_PRIVILEGES_HASH: + case PROC_PRIVILEGES_HASH: + case FUNC_PRIVILEGES_HASH: grant_name= (GRANT_NAME*) hash_element(grant_name_hash, idx); user= grant_name->user; host= grant_name->host.hostname; @@ -5498,86 +5513,60 @@ static int handle_grant_struct(uint struct_no, bool drop, if ( drop ) { switch ( struct_no ) { - case 0: + case USER_ACL: delete_dynamic_element(&acl_users, idx); + elements--; + /* + - If we are iterating through an array then we just have moved all + elements after the current element one position closer to its head. + This means that we have to take another look at the element at + current position as it is a new element from the array's tail. + - This is valid for USER_ACL, DB_ACL. + */ + idx--; break; - case 1: + case DB_ACL: delete_dynamic_element(&acl_dbs, idx); + elements--; + idx--; break; - case 2: - case 3: - case 4: - hash_delete(grant_name_hash, (uchar*) grant_name); + case COLUMN_PRIVILEGES_HASH: + case PROC_PRIVILEGES_HASH: + case FUNC_PRIVILEGES_HASH: + /* + Deleting while traversing a hash table is not valid procedure and + hence we save pointers to GRANT_NAME objects for later processing. + */ + if (acl_grant_name.append(grant_name)) + DBUG_RETURN(-1); break; } - elements--; - /* - - If we are iterating through an array then we just have moved all - elements after the current element one position closer to its head. - This means that we have to take another look at the element at - current position as it is a new element from the array's tail. - - If we are iterating through a hash the current element was replaced - with one of elements from the tail. So we also have to take a look - at the new element in current position. - Note that in our HASH implementation hash_delete() won't move any - elements with position after current one to position before the - current (i.e. from the tail to the head), so it is safe to continue - iteration without re-starting. - */ - idx--; } else if ( user_to ) { switch ( struct_no ) { - case 0: + case USER_ACL: acl_user->user= strdup_root(&mem, user_to->user.str); acl_user->host.hostname= strdup_root(&mem, user_to->host.str); break; - case 1: + case DB_ACL: acl_db->user= strdup_root(&mem, user_to->user.str); acl_db->host.hostname= strdup_root(&mem, user_to->host.str); break; - case 2: - case 3: - case 4: - { - /* - Save old hash key and its length to be able properly update - element position in hash. - */ - char *old_key= grant_name->hash_key; - size_t old_key_length= grant_name->key_length; - - /* - Update the grant structure with the new user name and host name. - */ - grant_name->set_user_details(user_to->host.str, grant_name->db, - user_to->user.str, grant_name->tname, - TRUE); - - /* - Since username is part of the hash key, when the user name - is renamed, the hash key is changed. Update the hash to - ensure that the position matches the new hash key value - */ - hash_update(grant_name_hash, (uchar*) grant_name, (uchar*) old_key, - old_key_length); - /* - hash_update() operation could have moved element from the tail - of the hash to the current position. So we need to take a look - at the element in current position once again. - Thanks to the fact that hash_update() for our HASH implementation - won't move any elements from the tail of the hash to the positions - before the current one (a.k.a. head) it is safe to continue - iteration without restarting. - */ - idx--; - break; - } + case COLUMN_PRIVILEGES_HASH: + case PROC_PRIVILEGES_HASH: + case FUNC_PRIVILEGES_HASH: + /* + Updating while traversing a hash table is not valid procedure and + hence we save pointers to GRANT_NAME objects for later processing. + */ + if (acl_grant_name.append(grant_name)) + DBUG_RETURN(-1); + break; } } else @@ -5586,6 +5575,48 @@ static int handle_grant_struct(uint struct_no, bool drop, break; } } + + if (drop || user_to) + { + /* + Traversing the elements stored in acl_grant_name dynamic array + to either delete or update them. + */ + for (int i= 0; i < acl_grant_name.elements(); ++i) + { + grant_name= acl_grant_name.at(i); + + if (drop) + { + my_hash_delete(grant_name_hash, (uchar *) grant_name); + } + else + { + /* + Save old hash key and its length to be able properly update + element position in hash. + */ + char *old_key= grant_name->hash_key; + size_t old_key_length= grant_name->key_length; + + /* + Update the grant structure with the new user name and host name. + */ + grant_name->set_user_details(user_to->host.str, grant_name->db, + user_to->user.str, grant_name->tname, + TRUE); + + /* + Since username is part of the hash key, when the user name + is renamed, the hash key is changed. Update the hash to + ensure that the position matches the new hash key value + */ + my_hash_update(grant_name_hash, (uchar*) grant_name, (uchar*) old_key, + old_key_length); + } + } + } + #ifdef EXTRA_DEBUG DBUG_PRINT("loop",("scan struct: %u result %d", struct_no, result)); #endif @@ -5623,6 +5654,7 @@ static int handle_grant_data(TABLE_LIST *tables, bool drop, { int result= 0; int found; + int ret; DBUG_ENTER("handle_grant_data"); /* Handle user table. */ @@ -5634,14 +5666,19 @@ static int handle_grant_data(TABLE_LIST *tables, bool drop, else { /* Handle user array. */ - if ((handle_grant_struct(0, drop, user_from, user_to) && ! result) || - found) + if (((ret= handle_grant_struct(USER_ACL, drop, user_from, user_to) > 0) && + ! result) || found) { result= 1; /* At least one record/element found. */ /* If search is requested, we do not need to search further. */ if (! drop && ! user_to) goto end; } + else if (ret < 0) + { + result= -1; + goto end; + } } /* Handle db table. */ @@ -5653,14 +5690,19 @@ static int handle_grant_data(TABLE_LIST *tables, bool drop, else { /* Handle db array. */ - if (((handle_grant_struct(1, drop, user_from, user_to) && ! result) || - found) && ! result) + if ((((ret= handle_grant_struct(DB_ACL, drop, user_from, user_to) > 0) && + ! result) || found) && ! result) { result= 1; /* At least one record/element found. */ /* If search is requested, we do not need to search further. */ if (! drop && ! user_to) goto end; } + else if (ret < 0) + { + result= -1; + goto end; + } } /* Handle stored routines table. */ @@ -5672,23 +5714,35 @@ static int handle_grant_data(TABLE_LIST *tables, bool drop, else { /* Handle procs array. */ - if (((handle_grant_struct(3, drop, user_from, user_to) && ! result) || - found) && ! result) + if ((((ret= handle_grant_struct(PROC_PRIVILEGES_HASH, drop, user_from, + user_to) > 0) && ! result) || found) && + ! result) { result= 1; /* At least one record/element found. */ /* If search is requested, we do not need to search further. */ if (! drop && ! user_to) goto end; } + else if (ret < 0) + { + result= -1; + goto end; + } /* Handle funcs array. */ - if (((handle_grant_struct(4, drop, user_from, user_to) && ! result) || - found) && ! result) + if ((((ret= handle_grant_struct(FUNC_PRIVILEGES_HASH, drop, user_from, + user_to) > 0) && ! result) || found) && + ! result) { result= 1; /* At least one record/element found. */ /* If search is requested, we do not need to search further. */ if (! drop && ! user_to) goto end; } + else if (ret < 0) + { + result= -1; + goto end; + } } /* Handle tables table. */ @@ -5716,9 +5770,12 @@ static int handle_grant_data(TABLE_LIST *tables, bool drop, else { /* Handle columns hash. */ - if (((handle_grant_struct(2, drop, user_from, user_to) && ! result) || - found) && ! result) + if ((((ret= handle_grant_struct(COLUMN_PRIVILEGES_HASH, drop, user_from, + user_to) > 0) && ! result) || found) && + ! result) result= 1; /* At least one record/element found. */ + else if (ret < 0) + result= -1; } } end: -- cgit v1.2.1 From 58de1660629890abfeeb50eb6bc9cf998c145894 Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Tue, 25 Sep 2012 13:09:53 +0200 Subject: Bug#14621627 THREAD CACHE IS UNFAIR When a client connects to a MySQL server, first a THD object is created. If there are any idle server threads waiting, the THD object is then added to a list and a server thread is woken up. This thread then retrieves the THD object from the list and starts executing. The problem was that this list of THD objects waiting for a server thread, was not working in a FIFO fashion, but rather LIFO. This is unfair, as it means that the last THD added (=last client connected) will be assigned a server thread first. Note however that for this to be a problem, several clients must be able to connect and have THD objects constructed before any server threads manages to be woken up. This is not a very likely scenario. This patch fixes the problem by changing the THD list to work FIFO rather than LIFO. This is the 5.1/5.5 version of the patch. --- sql/mysqld.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sql') diff --git a/sql/mysqld.cc b/sql/mysqld.cc index bc8d4162272..11e756381ab 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -4808,7 +4808,7 @@ void create_thread_to_handle_connection(THD *thd) if (cached_thread_count > wake_thread) { /* Get thread from cache */ - thread_cache.append(thd); + thread_cache.push_back(thd); wake_thread++; pthread_cond_signal(&COND_thread_cache); } -- cgit v1.2.1 From b079b388a50dfa1fdaa4da0cb22154b0cc6d885e Mon Sep 17 00:00:00 2001 From: Tor Didriksen Date: Tue, 25 Sep 2012 16:03:05 +0200 Subject: Backport Bug #11764313 57135: CRASH IN ITEM_FUNC_CASE::FIND_ITEM WITH CASE WHEN Bug #11764818 57692: Crash in item_func_in::val_int() with ZEROFILL --- sql/item_cmpfunc.cc | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) (limited to 'sql') diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 23f081e1cc0..6e8fa9a5f75 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2000, 2012, 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 @@ -3045,6 +3045,15 @@ void Item_func_case::fix_length_and_dec() return; } } + /* + Set cmp_context of all WHEN arguments. This prevents + Item_field::equal_fields_propagator() from transforming a + zerofill argument into a string constant. Such a change would + require rebuilding cmp_items. + */ + for (i= 0; i < ncases; i+= 2) + args[i]->cmp_context= item_cmp_type(left_result_type, + args[i]->result_type()); } if (else_expr_num == -1 || args[else_expr_num]->maybe_null) @@ -4032,6 +4041,16 @@ void Item_func_in::fix_length_and_dec() } } } + /* + Set cmp_context of all arguments. This prevents + Item_field::equal_fields_propagator() from transforming a zerofill integer + argument into a string constant. Such a change would require rebuilding + cmp_itmes. + */ + for (arg= args + 1, arg_end= args + arg_count; arg != arg_end ; arg++) + { + arg[0]->cmp_context= item_cmp_type(left_result_type, arg[0]->result_type()); + } max_length= 1; } -- cgit v1.2.1 From 540d0cd28eeb11abab578d189bbe748b930d8edc Mon Sep 17 00:00:00 2001 From: Tor Didriksen Date: Mon, 1 Oct 2012 13:12:38 +0200 Subject: Bug#14683676 ENDLESS MEMORY CONSUMPTION IN SETUP_REF_ARRAY WITH MAX IN SUBQUERY n_child_sum_items kept increasing. Since it is used for calculating the size of ref_pointer_array, we will allocate larger and larger chunks of memory, until we hit some operating system limit. The memory is free()d at disconnect, but is most likely *not* returned to the operating system. --- sql/sql_lex.cc | 1 + 1 file changed, 1 insertion(+) (limited to 'sql') diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index fd9367b99f2..e171cf1c06d 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -1616,6 +1616,7 @@ void st_select_lex::init_query() ref_pointer_array= 0; select_n_where_fields= 0; select_n_having_items= 0; + n_child_sum_items= 0; subquery_in_having= explicit_limit= 0; is_item_list_lookup= 0; first_execution= 1; -- cgit v1.2.1 From f1d3b0f19011217c13927f44a82e8e17291fbba7 Mon Sep 17 00:00:00 2001 From: Nuno Carvalho Date: Fri, 12 Oct 2012 08:32:10 +0100 Subject: BUG#14629727: USER_VAR_EVENT IS MISSING RANGE CHECKS This bug had two problems: P1) Reads out of bounds; P2) Writes out of bounds. PROBLEM P1 ---------- User_var_log_event unmarshalling from binlog was not performing range checks when using name_len and val_len variables to walk on event buffer. Added range checks to User_var_log_event unmarshalling to prevent unmarshalling errors. PROBLEM P2 ---------- User_var_log_event value was allocated on thread stack, what caused stack frame errors when User_var_log_event value was bigger than thread stack size. Currently value is allocated on heap memory. --- sql/log_event.cc | 45 ++++++++++++++++++++++++++++++++++++++++----- sql/log_event.h | 4 ++-- sql/mysql_priv.h | 44 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 86 insertions(+), 7 deletions(-) (limited to 'sql') diff --git a/sql/log_event.cc b/sql/log_event.cc index c3ba969cf1f..0ad258f7073 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -1286,7 +1286,7 @@ Log_event* Log_event::read_log_event(const char* buf, uint event_len, ev = new Rand_log_event(buf, description_event); break; case USER_VAR_EVENT: - ev = new User_var_log_event(buf, description_event); + ev = new User_var_log_event(buf, event_len, description_event); break; case FORMAT_DESCRIPTION_EVENT: ev = new Format_description_log_event(buf, event_len, description_event); @@ -5685,18 +5685,34 @@ void User_var_log_event::pack_info(Protocol* protocol) User_var_log_event:: -User_var_log_event(const char* buf, +User_var_log_event(const char* buf, uint event_len, const Format_description_log_event* description_event) :Log_event(buf, description_event) #ifndef MYSQL_CLIENT , deferred(false) #endif { + bool error= false; + const char* buf_start= buf; /* The Post-Header is empty. The Variable Data part begins immediately. */ buf+= description_event->common_header_len + description_event->post_header_len[USER_VAR_EVENT-1]; name_len= uint4korr(buf); name= (char *) buf + UV_NAME_LEN_SIZE; + + /* + We don't know yet is_null value, so we must assume that name_len + may have the bigger value possible, is_null= True and there is no + payload for val. + */ + if (0 == name_len || + !valid_buffer_range(name_len, buf_start, name, + event_len - UV_VAL_IS_NULL)) + { + error= true; + goto err; + } + buf+= UV_NAME_LEN_SIZE + name_len; is_null= (bool) *buf; if (is_null) @@ -5708,13 +5724,31 @@ User_var_log_event(const char* buf, } else { + if (!valid_buffer_range(UV_VAL_IS_NULL + UV_VAL_TYPE_SIZE + + UV_CHARSET_NUMBER_SIZE + UV_VAL_LEN_SIZE, + buf_start, buf, event_len)) + { + error= true; + goto err; + } + type= (Item_result) buf[UV_VAL_IS_NULL]; charset_number= uint4korr(buf + UV_VAL_IS_NULL + UV_VAL_TYPE_SIZE); val_len= uint4korr(buf + UV_VAL_IS_NULL + UV_VAL_TYPE_SIZE + UV_CHARSET_NUMBER_SIZE); val= (char *) (buf + UV_VAL_IS_NULL + UV_VAL_TYPE_SIZE + UV_CHARSET_NUMBER_SIZE + UV_VAL_LEN_SIZE); + + if (!valid_buffer_range(val_len, buf_start, val, event_len)) + { + error= true; + goto err; + } } + +err: + if (error) + name= 0; } @@ -5860,8 +5894,9 @@ void User_var_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info) char *hex_str; CHARSET_INFO *cs; - if (!(hex_str= (char *)my_alloca(2*val_len+1+2))) // 2 hex digits / byte - break; // no error, as we are 'void' + hex_str= (char *)my_malloc(2*val_len+1+2,MYF(MY_WME)); // 2 hex digits / byte + if (!hex_str) + return; str_to_hex(hex_str, val, val_len); /* For proper behaviour when mysqlbinlog|mysql, we need to explicitely @@ -5879,7 +5914,7 @@ void User_var_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info) my_b_printf(&cache, ":=_%s %s COLLATE `%s`%s\n", cs->csname, hex_str, cs->name, print_event_info->delimiter); - my_afree(hex_str); + my_free(hex_str, MYF(MY_WME)); } break; case ROW_RESULT: diff --git a/sql/log_event.h b/sql/log_event.h index ba6b9b876aa..c36564fcde8 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -2496,7 +2496,7 @@ public: void print(FILE* file, PRINT_EVENT_INFO* print_event_info); #endif - User_var_log_event(const char* buf, + User_var_log_event(const char* buf, uint event_len, const Format_description_log_event *description_event); ~User_var_log_event() {} Log_event_type get_type_code() { return USER_VAR_EVENT;} @@ -2510,7 +2510,7 @@ public: bool is_deferred() { return deferred; } void set_deferred() { deferred= true; } #endif - bool is_valid() const { return 1; } + bool is_valid() const { return name != 0; } private: #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 937617032dd..05a37228e17 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -501,6 +501,50 @@ protected: */ #define MAX_TIME_ZONE_NAME_LENGTH (NAME_LEN + 1) +/* + Check how many bytes are available on buffer. + + @param buf_start Pointer to buffer start. + @param buf_current Pointer to the current position on buffer. + @param buf_len Buffer length. + + @return Number of bytes available on event buffer. +*/ +template T available_buffer(const char* buf_start, + const char* buf_current, + T buf_len) +{ + return buf_len - (buf_current - buf_start); +} +/* Explicit instantion to unsigned int. */ +template unsigned int available_buffer(const char*, + const char*, + unsigned int); + +/* + Check if jump value is within buffer limits. + + @param jump Number of positions we want to advance. + @param buf_start Pointer to buffer start + @param buf_current Pointer to the current position on buffer. + @param buf_len Buffer length. + + @return True If jump value is within buffer limits. + False Otherwise. +*/ +template bool valid_buffer_range(T jump, + const char* buf_start, + const char* buf_current, + T buf_len) +{ + return (jump <= available_buffer(buf_start, buf_current, buf_len)); +} +/* Explicit instantion to unsigned int. */ +template bool valid_buffer_range(unsigned int, + const char*, + const char*, + unsigned int); + /* The rest of the file is included in the server only */ #ifndef MYSQL_CLIENT -- cgit v1.2.1 From fc1fbe159afa043d647711c83778d6911e5dfe39 Mon Sep 17 00:00:00 2001 From: Marc Alff Date: Fri, 12 Oct 2012 19:38:45 +0200 Subject: Bug#14629232 SECURITY VULNERABILITY WITH SHOW PROFILE This fix resolves a security vulnerability of SHOW PROFILE. See the bug report for details. --- sql/sql_profile.cc | 29 +++++++++++++++++++---------- sql/sql_profile.h | 6 ++++-- 2 files changed, 23 insertions(+), 12 deletions(-) (limited to 'sql') diff --git a/sql/sql_profile.cc b/sql/sql_profile.cc index 49666dde476..f3a95e6479b 100644 --- a/sql/sql_profile.cc +++ b/sql/sql_profile.cc @@ -39,6 +39,7 @@ #define TIME_I_S_DECIMAL_SIZE (TIME_FLOAT_DIGITS*100)+(TIME_FLOAT_DIGITS-3) #define MAX_QUERY_LENGTH 300 +#define MAX_QUERY_HISTORY 101 /* Reserved for systems that can't record the function name in source. */ const char * const _unknown_func_ = ""; @@ -233,9 +234,12 @@ void PROF_MEASUREMENT::collect() QUERY_PROFILE::QUERY_PROFILE(PROFILING *profiling_arg, const char *status_arg) :profiling(profiling_arg), profiling_query_id(0), query_source(NULL) { - profile_start= new PROF_MEASUREMENT(this, status_arg); - entries.push_back(profile_start); - profile_end= profile_start; + m_seq_counter= 1; + PROF_MEASUREMENT *prof= new PROF_MEASUREMENT(this, status_arg); + prof->m_seq= m_seq_counter++; + m_start_time_usecs= prof->time_usecs; + m_end_time_usecs= m_start_time_usecs; + entries.push_back(prof); } QUERY_PROFILE::~QUERY_PROFILE() @@ -275,9 +279,14 @@ void QUERY_PROFILE::new_status(const char *status_arg, else prof= new PROF_MEASUREMENT(this, status_arg); - profile_end= prof; + prof->m_seq= m_seq_counter++; + m_end_time_usecs= prof->time_usecs; entries.push_back(prof); + /* Maintain the query history size. */ + while (entries.elements > MAX_QUERY_HISTORY) + delete entries.pop(); + DBUG_VOID_RETURN; } @@ -437,8 +446,7 @@ bool PROFILING::show_profiles() String elapsed; - PROF_MEASUREMENT *ps= prof->profile_start; - PROF_MEASUREMENT *pe= prof->profile_end; + double query_time_usecs= prof->m_end_time_usecs - prof->m_start_time_usecs; if (++idx <= unit->offset_limit_cnt) continue; @@ -447,7 +455,7 @@ bool PROFILING::show_profiles() protocol->prepare_for_resend(); protocol->store((uint32)(prof->profiling_query_id)); - protocol->store((double)(pe->time_usecs - ps->time_usecs)/(1000.0*1000), + protocol->store((double)(query_time_usecs/(1000.0*1000)), (uint32) TIME_FLOAT_DIGITS-1, &elapsed); if (prof->query_source != NULL) protocol->store(prof->query_source, strlen(prof->query_source), @@ -507,17 +515,18 @@ int PROFILING::fill_statistics_info(THD *thd_arg, TABLE_LIST *tables, Item *cond us also include a numbering of each state per query. The query_id and the "seq" together are unique. */ - ulonglong seq; + ulong seq; void *entry_iterator; PROF_MEASUREMENT *entry, *previous= NULL; /* ...and for each query, go through all its state-change steps. */ - for (seq= 0, entry_iterator= query->entries.new_iterator(); + for (entry_iterator= query->entries.new_iterator(); entry_iterator != NULL; entry_iterator= query->entries.iterator_next(entry_iterator), - seq++, previous=entry, row_number++) + previous=entry, row_number++) { entry= query->entries.iterator_value(entry_iterator); + seq= entry->m_seq; /* Skip the first. We count spans of fence, not fence-posts. */ if (previous == NULL) continue; diff --git a/sql/sql_profile.h b/sql/sql_profile.h index b21216f290f..c59b342e502 100644 --- a/sql/sql_profile.h +++ b/sql/sql_profile.h @@ -182,6 +182,7 @@ private: char *file; unsigned int line; + ulong m_seq; double time_usecs; char *allocated_status_memory; @@ -213,8 +214,9 @@ private: query_id_t profiling_query_id; /* Session-specific id. */ char *query_source; - PROF_MEASUREMENT *profile_start; - PROF_MEASUREMENT *profile_end; + double m_start_time_usecs; + double m_end_time_usecs; + ulong m_seq_counter; Queue entries; -- cgit v1.2.1 From bdb4104cf6b658b0b614f2b5b3c690535fde3726 Mon Sep 17 00:00:00 2001 From: Neeraj Bisht Date: Tue, 16 Oct 2012 23:18:48 +0530 Subject: Bug#11745891 - LAST_INSERT(ID) DOES NOT SUPPORT BIGINT UNSIGNED Problem:- using last_insert_id() on an auto_incremented bigint unsigned does not work for values which are greater than max-bigint-signed. Analysis:- last_insert_id() returns the first auto_incremented value for a column and an auto_incremented value can have only positive values. In our code, when we are initializing a last_insert_id object, we are taking it as a signed BIGINT, So when the auto_incremented value reaches greater than max signed bigint, last_insert_id gives negative result. Solution: When we are fetching the value from last_insert_id, We are setting the unsigned_flag, so that it take only unsigned BIGINT value. sql/item_func.cc: here unsigned value is converted to signed value. sql/item_func.h: last_insert_id() gives an auto_incremented value which can be positive only,so defined it as a unsigned longlong sets the unsigned_flag to 1. --- sql/item_func.cc | 3 ++- sql/item_func.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'sql') diff --git a/sql/item_func.cc b/sql/item_func.cc index feb87fe5fd7..21efaf83aa8 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -3596,7 +3596,8 @@ longlong Item_func_last_insert_id::val_int() thd->first_successful_insert_id_in_prev_stmt= value; return value; } - return thd->read_first_successful_insert_id_in_prev_stmt(); + return + static_cast(thd->read_first_successful_insert_id_in_prev_stmt()); } diff --git a/sql/item_func.h b/sql/item_func.h index de1338b4081..ec410ed3d3d 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -1043,6 +1043,7 @@ public: const char *func_name() const { return "last_insert_id"; } void fix_length_and_dec() { + unsigned_flag= TRUE; if (arg_count) max_length= args[0]->max_length; } -- cgit v1.2.1 From eef1a1957e75a4153e44be26e0e32dee5f6069ad Mon Sep 17 00:00:00 2001 From: Neeraj Bisht Date: Thu, 18 Oct 2012 23:45:15 +0530 Subject: Bug#13726751 - 8 BYTE MEMORY LEAK IN DO_SAVE_BLOB Problem:- When we execute a query which has subquery with GROUP BY, ORDER BY and have a BLOB column,results a memory leak. Analysis:- In case of subquery, which have GROUP BY on BLOB and a ORDER BY on other field and BLOB is not a key. We allocate a tmp buffer to copy_field to take care of BLOB value.This copy_field value can have copies of its in two join(objects), so while freeing this copy_field we have to take care that it is not deleted twice. The double deletion of tmp_table_param.copy_field is handled by two patches. One by Kostja : revid:sp1r-konstantin@mysql.com-20050627101056-55153 Fix the broken test suite in -debug build. and other by Oleksandr revid:sp1r-bell@sanja.is.com.ua-20060118114857-19905 Excluded posibility of tmp_table_param.copy_field double deletion (BUG#14851). both of this patches are commited in different branch and while merging they both get placed,but there is no need for Kostja patch as Oleksandr patch handle this. sql/sql_select.cc: Bug13726751, tmp_join clean up is not necessary as later in the code we are taking care of cleaning up of tmp_join copy_field. --- sql/sql_select.cc | 7 ------- 1 file changed, 7 deletions(-) (limited to 'sql') diff --git a/sql/sql_select.cc b/sql/sql_select.cc index bcf601e5142..de9f0ead7a3 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -7084,15 +7084,8 @@ void JOIN::cleanup(bool full) } } } - /* - We are not using tables anymore - Unlock all tables. We may be in an INSERT .... SELECT statement. - */ if (full) { - if (tmp_join) - tmp_table_param.copy_field= 0; - /* Ensure that the following delete_elements() would not be called twice for the same list. -- cgit v1.2.1 From 62503f9cf5af989107e887925816add9b4aec950 Mon Sep 17 00:00:00 2001 From: Nuno Carvalho Date: Sun, 21 Oct 2012 20:28:19 +0100 Subject: BUG#14629727: USER_VAR_EVENT IS MISSING RANGE CHECKS Moved explicit instantiation of available_buffer and valid_buffer_range template functions to sql/log_event.cc. --- sql/log_event.cc | 16 ++++++++++++++++ sql/mysql_priv.h | 9 --------- 2 files changed, 16 insertions(+), 9 deletions(-) (limited to 'sql') diff --git a/sql/log_event.cc b/sql/log_event.cc index 0ad258f7073..58de0d310d7 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -54,6 +54,22 @@ */ #define FMT_G_BUFSIZE(PREC) (3 + (PREC) + 5 + 1) +/* + Explicit instantiation to unsigned int of template available_buffer + function. +*/ +template unsigned int available_buffer(const char*, + const char*, + unsigned int); + +/* + Explicit instantiation to unsigned int of template valid_buffer_range + function. +*/ +template bool valid_buffer_range(unsigned int, + const char*, + const char*, + unsigned int); #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) static int rows_event_stmt_cleanup(Relay_log_info const *rli, THD* thd); diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 05a37228e17..4741562cab3 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -516,10 +516,6 @@ template T available_buffer(const char* buf_start, { return buf_len - (buf_current - buf_start); } -/* Explicit instantion to unsigned int. */ -template unsigned int available_buffer(const char*, - const char*, - unsigned int); /* Check if jump value is within buffer limits. @@ -539,11 +535,6 @@ template bool valid_buffer_range(T jump, { return (jump <= available_buffer(buf_start, buf_current, buf_len)); } -/* Explicit instantion to unsigned int. */ -template bool valid_buffer_range(unsigned int, - const char*, - const char*, - unsigned int); /* The rest of the file is included in the server only */ #ifndef MYSQL_CLIENT -- cgit v1.2.1 From db1db8fa8cbcf95fdc2c77a744be7b2f9f31b170 Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Wed, 21 Nov 2012 21:55:04 -0800 Subject: Fixed LP bug #1002146 (bug mdev-645). If the setting of system variables does not allow to use join buffer for a join query with GROUP BY / ORDER BY then filesort is not needed if the first joined table is scanned in the order compatible with order specified by the list . --- sql/sql_select.cc | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'sql') diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 1e57f11e399..a3d3f84806c 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -7422,8 +7422,9 @@ get_best_combination(JOIN *join) if ( !(keyuse= join->best_positions[tablenr].key)) { j->type=JT_ALL; - if (tablenr != join->const_tables) - join->full_join=1; + if (join->best_positions[tablenr].use_join_buffer && + tablenr != join->const_tables) + join->full_join= 1; } /*if (join->best_positions[tablenr].sj_strategy == SJ_OPT_LOOSE_SCAN) @@ -8436,7 +8437,9 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) We will use join cache here : prevent sorting of the first table only and sort at the end. */ - if (i != join->const_tables && join->table_count > join->const_tables + 1) + if (i != join->const_tables && + join->table_count > join->const_tables + 1 && + join->best_positions[i].use_join_buffer) join->full_join= 1; } -- cgit v1.2.1 From 2cbf2e643bc9b6e048905ba4d49da875f45018e7 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 29 Nov 2012 19:34:47 +0100 Subject: applying patch for BUG15912213 --- sql/sql_acl.cc | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) (limited to 'sql') diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index e07f668b1cf..80662832140 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -1356,10 +1356,19 @@ ulong acl_get(const char *host, const char *ip, { ulong host_access= ~(ulong)0, db_access= 0; uint i; - size_t key_length; + size_t key_length, copy_length; char key[ACL_KEY_LENGTH],*tmp_db,*end; acl_entry *entry; DBUG_ENTER("acl_get"); + + copy_length= (size_t) (strlen(ip ? ip : "") + + strlen(user ? user : "") + + strlen(db ? db : "")); + /* + Make sure that strmov() operations do not result in buffer overflow. + */ + if (copy_length >= ACL_KEY_LENGTH) + DBUG_RETURN(0); VOID(pthread_mutex_lock(&acl_cache->lock)); end=strmov((tmp_db=strmov(strmov(key, ip ? ip : "")+1,user)+1),db); @@ -4340,6 +4349,16 @@ bool check_grant_db(THD *thd,const char *db) char helping [NAME_LEN+USERNAME_LENGTH+2]; uint len; bool error= TRUE; + size_t copy_length; + + copy_length= (size_t) (strlen(sctx->priv_user ? sctx->priv_user : "") + + strlen(db ? db : "")); + + /* + Make sure that strmov() operations do not result in buffer overflow. + */ + if (copy_length >= (NAME_LEN+USERNAME_LENGTH+2)) + return 1; len= (uint) (strmov(strmov(helping, sctx->priv_user) + 1, db) - helping) + 1; -- cgit v1.2.1 From eff07bf08e29afab76c7688ec063ef6881ee464f Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Tue, 4 Dec 2012 17:08:02 +0100 Subject: proactive s/strmov/strnmov/ in sql_acl.cc and related test cases --- sql/sql_acl.cc | 37 ++++++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-) (limited to 'sql') diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 020aa042722..4f4c0eeb06b 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -341,7 +341,12 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) convert db to lower case and give a warning if the db wasn't already in lower case */ - (void) strmov(tmp_name, host.db); + char *end = strnmov(tmp_name, host.db, sizeof(tmp_name)); + if (end >= tmp_name + sizeof(tmp_name)) + { + sql_print_warning(ER(ER_WRONG_DB_NAME), host.db); + continue; + } my_casedn_str(files_charset_info, host.db); if (strcmp(host.db, tmp_name) != 0) sql_print_warning("'host' entry '%s|%s' had database in mixed " @@ -595,7 +600,12 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) convert db to lower case and give a warning if the db wasn't already in lower case */ - (void)strmov(tmp_name, db.db); + char *end = strnmov(tmp_name, db.db, sizeof(tmp_name)); + if (end >= tmp_name + sizeof(tmp_name)) + { + sql_print_warning(ER(ER_WRONG_DB_NAME), db.db); + continue; + } my_casedn_str(files_charset_info, db.db); if (strcmp(db.db, tmp_name) != 0) { @@ -2474,15 +2484,23 @@ static GRANT_NAME *name_hash_search(HASH *name_hash, const char *user, const char *tname, bool exact, bool name_tolower) { - char helping [SAFE_NAME_LEN*2+USERNAME_LENGTH+3], *name_ptr; + char helping[SAFE_NAME_LEN*2+USERNAME_LENGTH+3]; + char *hend = helping + sizeof(helping); uint len; GRANT_NAME *grant_name,*found=0; HASH_SEARCH_STATE state; - name_ptr= strmov(strmov(helping, user) + 1, db) + 1; - len = (uint) (strmov(name_ptr, tname) - helping) + 1; + char *db_ptr= strmov(helping, user) + 1; + char *tname_ptr= strnmov(db_ptr, db, hend - db_ptr) + 1; + if (tname_ptr > hend) + return 0; // invalid name = not found + char *end= strnmov(tname_ptr, tname, hend - tname_ptr) + 1; + if (end > hend) + return 0; // invalid name = not found + + len = (uint) (end - helping); if (name_tolower) - my_casedn_str(files_charset_info, name_ptr); + my_casedn_str(files_charset_info, tname_ptr); for (grant_name= (GRANT_NAME*) hash_first(name_hash, (uchar*) helping, len, &state); grant_name ; @@ -3466,7 +3484,12 @@ bool mysql_grant(THD *thd, const char *db, List &list, if (lower_case_table_names && db) { - strmov(tmp_db,db); + char *end= strnmov(tmp_db,db, sizeof(tmp_db)); + if (end >= tmp_db + sizeof(tmp_db)) + { + my_error(ER_WRONG_DB_NAME ,MYF(0), db); + DBUG_RETURN(TRUE); + } my_casedn_str(files_charset_info, tmp_db); db=tmp_db; } -- cgit v1.2.1 From 0aad592f49f0fb790f712aa6a644653cf9a0218f Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 5 Dec 2012 21:06:00 +0200 Subject: MDEV-3914 fix. Fixed algorithm of detecting of first real table in view/subquery-in-the-FROM-clase. --- sql/table.cc | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) (limited to 'sql') diff --git a/sql/table.cc b/sql/table.cc index d42fd14120c..abdd0b0f9e6 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -4457,19 +4457,33 @@ TABLE *TABLE_LIST::get_real_join_table() DBUG_ASSERT(tbl->derived == NULL || tbl->derived->first_select()->next_select() == NULL); - if (tbl->table) - table= tbl->table; - tbl= (tbl->view != NULL ? - tbl->view->select_lex.get_table_list() : - tbl->derived->first_select()->get_table_list()); - - /* find left table in outer join on this level */ - while(tbl->outer_join & JOIN_TYPE_RIGHT) { - DBUG_ASSERT(tbl->next_local); - tbl= tbl->next_local; + List_iterator_fast ti; + { + List_iterator_fast + ti(tbl->view != NULL ? + tbl->view->select_lex.top_join_list : + tbl->derived->first_select()->top_join_list); + for (;;) + { + tbl= NULL; + /* + Find left table in outer join on this level + (the list is reverted). + */ + for (TABLE_LIST *t= ti++; t; t= ti++) + tbl= t; + /* + It is impossible that the list is empty + so tbl can't be NULL after above loop. + */ + if (!tbl->nested_join) + break; + /* go deeper if we've found nested join */ + ti= tbl->nested_join->join_list; + } + } } - } return tbl->table; -- cgit v1.2.1 From d07b5f1ca295d4eb6eeba0b88c93f04e9e21cb5c Mon Sep 17 00:00:00 2001 From: Joerg Bruehe Date: Fri, 7 Dec 2012 10:47:57 +0100 Subject: Last-minute fix to 5.1.67, taking a change done to main 5.1 by Dmitri Lenev. This is the original comment: > committer: Dmitry Lenev > branch nick: mysql-5.1-15954896 > timestamp: Wed 2012-12-05 19:26:56 +0400 > message: > Bug #15954896 "SP, MULTI-TABLE DELETE AND LONG ALIAS". Using too long table aliases in stored routines might have caused server crashes. Code in sp_head::merge_table_list() which is responsible for collecting information about tables used in stored routine was not aware of the fact that table alias might have arbitrary length. I.e. it assumed that table alias can't be longer than NAME_LEN bytes and allocated buffer for a key identifying table accordingly. This patch fixes the issue by ensuring that we use dynamically allocated buffer for table key when table alias is too long. By default stack based buffer is used in which NAME_LEN bytes are reserved for table alias. --- sql/sp_head.cc | 50 ++++++++++++++++++++++++++++---------------------- 1 file changed, 28 insertions(+), 22 deletions(-) (limited to 'sql') diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 7eef9f5ab28..054fc5e223e 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -3839,8 +3839,6 @@ typedef struct st_sp_table Multi-set key: db_name\0table_name\0alias\0 - for normal tables db_name\0table_name\0 - for temporary tables - Note that in both cases we don't take last '\0' into account when - we count length of key. */ LEX_STRING qname; uint db_length, table_name_length; @@ -3897,19 +3895,26 @@ sp_head::merge_table_list(THD *thd, TABLE_LIST *table, LEX *lex_for_tmp_check) for (; table ; table= table->next_global) if (!table->derived && !table->schema_table) { - char tname[(NAME_LEN + 1) * 3]; // db\0table\0alias\0 - uint tlen, alen; - - tlen= table->db_length; - memcpy(tname, table->db, tlen); - tname[tlen++]= '\0'; - memcpy(tname+tlen, table->table_name, table->table_name_length); - tlen+= table->table_name_length; - tname[tlen++]= '\0'; - alen= strlen(table->alias); - memcpy(tname+tlen, table->alias, alen); - tlen+= alen; - tname[tlen]= '\0'; + /* + Structure of key for the multi-set is "db\0table\0alias\0". + Since "alias" part can have arbitrary length we use String + object to construct the key. By default String will use + buffer allocated on stack with NAME_LEN bytes reserved for + alias, since in most cases it is going to be smaller than + NAME_LEN bytes. + */ + char tname_buff[(NAME_LEN + 1) * 3]; + String tname(tname_buff, sizeof(tname_buff), &my_charset_bin); + uint temp_table_key_length; + + tname.length(0); + tname.append(table->db, table->db_length); + tname.append('\0'); + tname.append(table->table_name, table->table_name_length); + tname.append('\0'); + temp_table_key_length= tname.length(); + tname.append(table->alias); + tname.append('\0'); /* Upgrade the lock type because this table list will be used @@ -3924,9 +3929,10 @@ sp_head::merge_table_list(THD *thd, TABLE_LIST *table, LEX *lex_for_tmp_check) (and therefore should not be prelocked). Otherwise we will erroneously treat table with same name but with different alias as non-temporary. */ - if ((tab= (SP_TABLE *)hash_search(&m_sptabs, (uchar *)tname, tlen)) || - ((tab= (SP_TABLE *)hash_search(&m_sptabs, (uchar *)tname, - tlen - alen - 1)) && + if ((tab= (SP_TABLE *)hash_search(&m_sptabs, (uchar *)tname.ptr(), + tname.length())) || + ((tab= (SP_TABLE *)hash_search(&m_sptabs, (uchar *)tname.ptr(), + temp_table_key_length)) && tab->temp)) { if (tab->lock_type < table->lock_type) @@ -3945,11 +3951,11 @@ sp_head::merge_table_list(THD *thd, TABLE_LIST *table, LEX *lex_for_tmp_check) lex_for_tmp_check->create_info.options & HA_LEX_CREATE_TMP_TABLE) { tab->temp= TRUE; - tab->qname.length= tlen - alen - 1; + tab->qname.length= temp_table_key_length; } else - tab->qname.length= tlen; - tab->qname.str= (char*) thd->memdup(tname, tab->qname.length + 1); + tab->qname.length= tname.length(); + tab->qname.str= (char*) thd->memdup(tname.ptr(), tab->qname.length); if (!tab->qname.str) return FALSE; tab->table_name_length= table->table_name_length; @@ -4018,7 +4024,7 @@ sp_head::add_used_tables_to_table_list(THD *thd, if (!(tab_buff= (char *)thd->calloc(ALIGN_SIZE(sizeof(TABLE_LIST)) * stab->lock_count)) || !(key_buff= (char*)thd->memdup(stab->qname.str, - stab->qname.length + 1))) + stab->qname.length))) DBUG_RETURN(FALSE); for (uint j= 0; j < stab->lock_count; j++) -- cgit v1.2.1 From e99aa91e90adfd54cc1f460dd8cdd19614f30abc Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 19 Dec 2012 15:56:57 +0200 Subject: MDEV-3928: Assertion `example' failed in Item_cache::is_expensive_processor with a 2-level IN subquery Analysis: The following call stack shows that it is possible to set Item_cache::value_cached, and the relevant value without setting Item_cache::example. #0 Item_cache_temporal::store_packed at item.cc:8395 #1 get_datetime_value at item_cmpfunc.cc:915 #2 resolve_const_item at item.cc:7987 #3 propagate_cond_constants at sql_select.cc:12264 #4 propagate_cond_constants at sql_select.cc:12227 #5 optimize_cond at sql_select.cc:13026 #6 JOIN::optimize at sql_select.cc:1016 #7 st_select_lex::optimize_unflattened_subqueries at sql_lex.cc:3161 #8 JOIN::optimize_unflattened_subqueries at opt_subselect.cc:4880 #9 JOIN::optimize at sql_select.cc:1554 The fix is to set Item_cache_temporal::example even when the value is set directly by Item_cache_temporal::store_packed. This makes the Item_cache_temporal object consistent. --- sql/item.cc | 3 ++- sql/item.h | 4 ++-- sql/item_cmpfunc.cc | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) (limited to 'sql') diff --git a/sql/item.cc b/sql/item.cc index 98c27266415..16dbd011f22 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -8388,9 +8388,10 @@ int Item_cache_temporal::save_in_field(Field *field, bool no_conversions) } -void Item_cache_temporal::store_packed(longlong val_arg) +void Item_cache_temporal::store_packed(longlong val_arg, Item *example) { /* An explicit values is given, save it. */ + store(example); value_cached= true; value= val_arg; null_value= false; diff --git a/sql/item.h b/sql/item.h index 6324e900f24..0852287cc7f 100644 --- a/sql/item.h +++ b/sql/item.h @@ -3799,7 +3799,7 @@ public: bool cache_value(); bool get_date(MYSQL_TIME *ltime, uint fuzzydate); int save_in_field(Field *field, bool no_conversions); - void store_packed(longlong val_arg); + void store_packed(longlong val_arg, Item *example); /* Having a clone_item method tells optimizer that this object is a constant and need not be optimized further. @@ -3808,7 +3808,7 @@ public: Item *clone_item() { Item_cache_temporal *item= new Item_cache_temporal(cached_field_type); - item->store_packed(value); + item->store_packed(value, example); return item; } }; diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index fed246ef812..b3b20a55ed9 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -912,7 +912,7 @@ get_datetime_value(THD *thd, Item ***item_arg, Item **cache_arg, if (save_arena) thd->set_query_arena(save_arena); - cache->store_packed(value); + cache->store_packed(value, item); *cache_arg= cache; *item_arg= cache_arg; } -- cgit v1.2.1 From 6f26aac9409e3456798e58a4ee4306e43c7ebf7b Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 28 Dec 2012 14:41:46 +0200 Subject: MDEV-3873 & MDEV-3876 & MDEV-3912 : Wrong result (extra rows) with ALL subquery from a MERGE view. The problem was in the lost ability to be null for the table of a left join if it is a view/derived table. It hapenned because setup_table_map(), was called earlier then we merged the view or derived. Fixed by propagating new maybe_null flag during Item::update_used_tables(). Change in join_outer.test and join_outer_jcl6.test appeared because IS NULL reported no used tables (i.e. constant) for argument which could not be NULL and new maybe_null flag was propagated for IS NULL argument (Item_field) because table the Item_field belonged to changed its maybe_null status. --- sql/item.cc | 11 +++++++++-- sql/item.h | 16 +++++++++++++--- sql/item_cmpfunc.cc | 20 ++++++++++++-------- sql/item_cmpfunc.h | 28 +++++++++++++++++++++++++--- sql/item_func.cc | 25 ++++++++++++++----------- sql/item_func.h | 48 +++++++++++++++++++++++++++++------------------- sql/item_geofunc.cc | 4 ++-- sql/item_geofunc.h | 30 +++++++++++++++--------------- sql/item_strfunc.cc | 26 +++++++++++++------------- sql/item_strfunc.h | 28 +++++++++++++++------------- sql/item_timefunc.cc | 10 +++++----- sql/item_timefunc.h | 40 ++++++++++++++++++++-------------------- sql/item_xmlfunc.cc | 1 + sql/item_xmlfunc.h | 8 ++------ 14 files changed, 175 insertions(+), 120 deletions(-) (limited to 'sql') diff --git a/sql/item.cc b/sql/item.cc index 16dbd011f22..aee35b611e7 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -9166,11 +9166,18 @@ table_map Item_ref::used_tables() const void Item_ref::update_used_tables() -{ +{ if (!get_depended_from()) - (*ref)->update_used_tables(); + (*ref)->update_used_tables(); + maybe_null= (*ref)->maybe_null; } +void Item_direct_view_ref::update_used_tables() +{ + Item_ref::update_used_tables(); + if (view->table && view->table->maybe_null) + maybe_null= TRUE; +} table_map Item_direct_view_ref::used_tables() const { diff --git a/sql/item.h b/sql/item.h index 0852287cc7f..2b5e867feb7 100644 --- a/sql/item.h +++ b/sql/item.h @@ -1849,9 +1849,14 @@ public: bitmap_fast_test_and_set(tab->read_set, field->field_index); if (field->vcol_info) tab->mark_virtual_col(field); - } + } + } + void update_used_tables() + { + update_table_bitmaps(); + if (field && field->table) + maybe_null= field->maybe_null(); } - void update_used_tables() { update_table_bitmaps(); } Item *get_tmp_table_item(THD *thd); bool collect_item_field_processor(uchar * arg); bool add_field_to_set_processor(uchar * arg); @@ -2874,7 +2879,11 @@ public: enum Item_result result_type () const { return orig_item->result_type(); } enum_field_types field_type() const { return orig_item->field_type(); } table_map used_tables() const { return orig_item->used_tables(); } - void update_used_tables() { orig_item->update_used_tables(); } + void update_used_tables() + { + orig_item->update_used_tables(); + maybe_null= orig_item->maybe_null; + } bool const_item() const { return orig_item->const_item(); } table_map not_null_tables() const { return orig_item->not_null_tables(); } bool walk(Item_processor processor, bool walk_subquery, uchar *arg) @@ -2966,6 +2975,7 @@ public: Item *replace_equal_field(uchar *arg); table_map used_tables() const; table_map not_null_tables() const; + void update_used_tables(); bool walk(Item_processor processor, bool walk_subquery, uchar *arg) { return (*ref)->walk(processor, walk_subquery, arg) || diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index b3b20a55ed9..75d3f31d4cd 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -1351,7 +1351,7 @@ int Arg_comparator::compare_e_row() void Item_func_truth::fix_length_and_dec() { - maybe_null= 0; + set_persist_maybe_null(0); null_value= 0; decimals= 0; max_length= 1; @@ -1859,7 +1859,8 @@ longlong Item_func_eq::val_int() void Item_func_equal::fix_length_and_dec() { Item_bool_func2::fix_length_and_dec(); - maybe_null=null_value=0; + set_persist_maybe_null(0); + null_value= 0; } longlong Item_func_equal::val_int() @@ -1999,7 +2000,7 @@ void Item_func_interval::fix_length_and_dec() } } } - maybe_null= 0; + set_persist_maybe_null(0); max_length= 2; used_tables_cache|= row->used_tables(); not_null_tables_cache= row->not_null_tables(); @@ -2670,7 +2671,7 @@ void Item_func_nullif::fix_length_and_dec() { Item_bool_func2::fix_length_and_dec(); - maybe_null=1; + set_persist_maybe_null(1); if (args[0]) // Only false if EOM { max_length=args[0]->max_length; @@ -4465,6 +4466,8 @@ void Item_cond::update_used_tables() item->update_used_tables(); used_tables_cache|= item->used_tables(); const_item_cache&= item->const_item(); + if (!persistent_maybe_null && item->maybe_null) + maybe_null= 1; } } @@ -4639,10 +4642,9 @@ longlong Item_is_not_null_test::val_int() */ void Item_is_not_null_test::update_used_tables() { + args[0]->update_used_tables(); if (!args[0]->maybe_null) used_tables_cache= 0; /* is always true */ - else - args[0]->update_used_tables(); } @@ -4925,7 +4927,7 @@ Item_func_regex::fix_fields(THD *thd, Item **ref) int comp_res= regcomp(TRUE); if (comp_res == -1) { // Will always return NULL - maybe_null=1; + set_persist_maybe_null(1); fixed= 1; return FALSE; } @@ -4935,7 +4937,7 @@ Item_func_regex::fix_fields(THD *thd, Item **ref) maybe_null= args[0]->maybe_null; } else - maybe_null=1; + set_persist_maybe_null(1); fixed= 1; return FALSE; } @@ -5729,6 +5731,8 @@ void Item_equal::update_used_tables() item->update_used_tables(); used_tables_cache|= item->used_tables(); const_item_cache&= item->const_item(); + if (!persistent_maybe_null && item->maybe_null) + maybe_null= 1; } } diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 143fef87abd..dca139e6321 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -757,6 +757,11 @@ public: my_decimal *decimal_op(my_decimal *); enum_field_types field_type() const; void fix_length_and_dec(); + void update_used_tables() + { + Item_func_coalesce::update_used_tables(); + maybe_null= args[1]->maybe_null; + } const char *func_name() const { return "ifnull"; } Field *tmp_table_field(TABLE *table); uint decimal_precision() const; @@ -779,6 +784,11 @@ public: enum_field_types field_type() const { return cached_field_type; } bool fix_fields(THD *, Item **); void fix_length_and_dec(); + void update_used_tables() + { + Item_func::update_used_tables(); + maybe_null= args[1]->maybe_null || args[2]->maybe_null; + } uint decimal_precision() const; const char *func_name() const { return "if"; } bool eval_not_null_tables(uchar *opt_arg); @@ -1242,6 +1252,12 @@ public: my_decimal *val_decimal(my_decimal *); bool fix_fields(THD *thd, Item **ref); void fix_length_and_dec(); + void update_used_tables() + { + Item_func::update_used_tables(); + if (else_expr_num == -1 || args[else_expr_num]->maybe_null) + maybe_null= 1; + } uint decimal_precision() const; table_map not_null_tables() const { return 0; } enum Item_result result_type () const { return cached_result_type; } @@ -1363,13 +1379,14 @@ public: enum Functype functype() const { return ISNULL_FUNC; } void fix_length_and_dec() { - decimals=0; max_length=1; maybe_null=0; + decimals=0; max_length=1; set_persist_maybe_null(0); update_used_tables(); } const char *func_name() const { return "isnull"; } /* Optimize case of not_null_column IS NULL */ virtual void update_used_tables() { + args[0]->update_used_tables(); if (!args[0]->maybe_null) { used_tables_cache= 0; /* is always false */ @@ -1377,7 +1394,6 @@ public: } else { - args[0]->update_used_tables(); used_tables_cache= args[0]->used_tables(); const_item_cache= args[0]->const_item(); } @@ -1424,7 +1440,7 @@ public: enum Functype functype() const { return ISNOTNULL_FUNC; } void fix_length_and_dec() { - decimals=0; max_length=1; maybe_null=0; + decimals=0; max_length=1; set_persist_maybe_null(0); } const char *func_name() const { return "isnotnull"; } optimize_type select_optimize() const { return OPTIMIZE_NULL; } @@ -1495,6 +1511,12 @@ public: void cleanup(); longlong val_int(); bool fix_fields(THD *thd, Item **ref); + void update_used_tables() + { + Item_bool_func::update_used_tables(); + if (regex_is_const) + maybe_null= 1; + } const char *func_name() const { return "regexp"; } virtual inline void print(String *str, enum_query_type query_type) diff --git a/sql/item_func.cc b/sql/item_func.cc index 42332d6b1b2..0cd96619023 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -86,7 +86,7 @@ void Item_func::set_arguments(List &list) } Item_func::Item_func(List &list) - :allowed_arg_cols(1) + :allowed_arg_cols(1), persistent_maybe_null(0) { set_arguments(list); } @@ -94,6 +94,7 @@ Item_func::Item_func(List &list) Item_func::Item_func(THD *thd, Item_func *item) :Item_result_field(thd, item), allowed_arg_cols(item->allowed_arg_cols), + persistent_maybe_null(0), arg_count(item->arg_count), used_tables_cache(item->used_tables_cache), not_null_tables_cache(item->not_null_tables_cache), @@ -423,6 +424,8 @@ void Item_func::update_used_tables() args[i]->update_used_tables(); used_tables_cache|=args[i]->used_tables(); const_item_cache&=args[i]->const_item(); + if (!persistent_maybe_null && args[i]->maybe_null) + maybe_null= 1; } } @@ -1496,7 +1499,7 @@ void Item_func_div::fix_length_and_dec() case IMPOSSIBLE_RESULT: DBUG_ASSERT(0); } - maybe_null= 1; // devision by zero + set_persist_maybe_null(1); // devision by zero DBUG_VOID_RETURN; } @@ -1531,7 +1534,7 @@ void Item_func_int_div::fix_length_and_dec() max_length=args[0]->max_length - (argtype == DECIMAL_RESULT || argtype == INT_RESULT ? args[0]->decimals : 0); - maybe_null=1; + set_persist_maybe_null(1); unsigned_flag=args[0]->unsigned_flag | args[1]->unsigned_flag; } @@ -1612,7 +1615,7 @@ void Item_func_mod::result_precision() void Item_func_mod::fix_length_and_dec() { Item_num_op::fix_length_and_dec(); - maybe_null= 1; + set_persist_maybe_null(1); unsigned_flag= args[0]->unsigned_flag; } @@ -2778,7 +2781,7 @@ longlong Item_func_field::val_int() void Item_func_field::fix_length_and_dec() { - maybe_null=0; max_length=3; + set_persist_maybe_null(0); max_length=3; cmp_type= args[0]->result_type(); for (uint i=1; i < arg_count ; i++) cmp_type= item_cmp_type(cmp_type, args[i]->result_type()); @@ -4843,7 +4846,7 @@ void Item_func_get_user_var::fix_length_and_dec() { THD *thd=current_thd; int error; - maybe_null=1; + set_persist_maybe_null(1); decimals=NOT_FIXED_DEC; max_length=MAX_BLOB_WIDTH; @@ -5049,7 +5052,7 @@ void Item_func_get_system_var::update_null_value() void Item_func_get_system_var::fix_length_and_dec() { char *cptr; - maybe_null= TRUE; + set_persist_maybe_null(1); max_length= 0; if (var->check_type(var_type)) @@ -5580,7 +5583,7 @@ bool Item_func_match::fix_fields(THD *thd, Item **ref) DBUG_ASSERT(fixed == 0); Item *UNINIT_VAR(item); // Safe as arg_count is > 1 - maybe_null=1; + set_persist_maybe_null(1); join_key=0; /* @@ -5914,7 +5917,7 @@ longlong Item_func_row_count::val_int() Item_func_sp::Item_func_sp(Name_resolution_context *context_arg, sp_name *name) :Item_func(), context(context_arg), m_name(name), m_sp(NULL), sp_result_field(NULL) { - maybe_null= 1; + set_persist_maybe_null(1); m_name->init_qname(current_thd); dummy_table= (TABLE*) sql_calloc(sizeof(TABLE)+ sizeof(TABLE_SHARE)); dummy_table->s= (TABLE_SHARE*) (dummy_table+1); @@ -5925,7 +5928,7 @@ Item_func_sp::Item_func_sp(Name_resolution_context *context_arg, sp_name *name, List &list) :Item_func(list), context(context_arg), m_name(name), m_sp(NULL),sp_result_field(NULL) { - maybe_null= 1; + set_persist_maybe_null(1); m_name->init_qname(current_thd); dummy_table= (TABLE*) sql_calloc(sizeof(TABLE)+ sizeof(TABLE_SHARE)); dummy_table->s= (TABLE_SHARE*) (dummy_table+1); @@ -6066,7 +6069,7 @@ void Item_func_sp::fix_length_and_dec() decimals= sp_result_field->decimals(); max_length= sp_result_field->field_length; collation.set(sp_result_field->charset()); - maybe_null= 1; + set_persist_maybe_null(1); unsigned_flag= test(sp_result_field->flags & UNSIGNED_FLAG); DBUG_VOID_RETURN; diff --git a/sql/item_func.h b/sql/item_func.h index 082c9b83296..3ab1fbad4e3 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -38,6 +38,8 @@ protected: 0 means get this number from first argument */ uint allowed_arg_cols; + /* maybe_null can't be changed by parameters or used table state */ + bool persistent_maybe_null; public: uint arg_count; table_map used_tables_cache, not_null_tables_cache; @@ -63,13 +65,13 @@ public: enum Type type() const { return FUNC_ITEM; } virtual enum Functype functype() const { return UNKNOWN_FUNC; } Item_func(void): - allowed_arg_cols(1), arg_count(0) + allowed_arg_cols(1), persistent_maybe_null(0), arg_count(0) { with_sum_func= 0; with_field= 0; } Item_func(Item *a): - allowed_arg_cols(1), arg_count(1) + allowed_arg_cols(1), persistent_maybe_null(0), arg_count(1) { args= tmp_arg; args[0]= a; @@ -77,7 +79,7 @@ public: with_field= a->with_field; } Item_func(Item *a,Item *b): - allowed_arg_cols(1), arg_count(2) + allowed_arg_cols(1), persistent_maybe_null(0), arg_count(2) { args= tmp_arg; args[0]= a; args[1]= b; @@ -85,7 +87,7 @@ public: with_field= a->with_field || b->with_field; } Item_func(Item *a,Item *b,Item *c): - allowed_arg_cols(1) + allowed_arg_cols(1), persistent_maybe_null(0) { arg_count= 0; if ((args= (Item**) sql_alloc(sizeof(Item*)*3))) @@ -97,7 +99,7 @@ public: } } Item_func(Item *a,Item *b,Item *c,Item *d): - allowed_arg_cols(1) + allowed_arg_cols(1), persistent_maybe_null(0) { arg_count= 0; if ((args= (Item**) sql_alloc(sizeof(Item*)*4))) @@ -111,7 +113,7 @@ public: } } Item_func(Item *a,Item *b,Item *c,Item *d,Item* e): - allowed_arg_cols(1) + allowed_arg_cols(1), persistent_maybe_null(0) { arg_count= 5; if ((args= (Item**) sql_alloc(sizeof(Item*)*5))) @@ -300,6 +302,11 @@ public: info.bool_function= &Item::restore_to_before_no_rows_in_result; walk(&Item::call_bool_func_processor, FALSE, (uchar*) &info); } + inline void set_persist_maybe_null(bool mb_null) + { + maybe_null= mb_null; + persistent_maybe_null= 1; + } }; @@ -506,7 +513,7 @@ public: } double val_real(); enum_field_types field_type() const { return MYSQL_TYPE_DOUBLE; } - void fix_length_and_dec() { maybe_null= 1; } + void fix_length_and_dec() { set_persist_maybe_null(1); } const char *func_name() const { return "double_typecast"; } virtual void print(String *str, enum_query_type query_type); }; @@ -647,7 +654,7 @@ class Item_dec_func :public Item_real_func void fix_length_and_dec() { decimals=NOT_FIXED_DEC; max_length=float_length(decimals); - maybe_null=1; + set_persist_maybe_null(1); } }; @@ -971,7 +978,7 @@ public: Item_func_coercibility(Item *a) :Item_int_func(a) {} longlong val_int(); const char *func_name() const { return "coercibility"; } - void fix_length_and_dec() { max_length=10; maybe_null= 0; } + void fix_length_and_dec() { max_length=10; set_persist_maybe_null(0); } table_map not_null_tables() const { return 0; } }; @@ -1132,7 +1139,7 @@ public: {} longlong val_int(); const char *func_name() const { return "benchmark"; } - void fix_length_and_dec() { max_length=1; maybe_null=0; } + void fix_length_and_dec() { max_length=1; set_persist_maybe_null(0); } virtual void print(String *str, enum_query_type query_type); bool check_vcol_func_processor(uchar *int_arg) { @@ -1385,7 +1392,7 @@ public: double val_real() { DBUG_ASSERT(fixed == 1); null_value= 1; return 0.0; } longlong val_int() { DBUG_ASSERT(fixed == 1); null_value=1; return 0; } enum Item_result result_type () const { return STRING_RESULT; } - void fix_length_and_dec() { maybe_null=1; max_length=0; } + void fix_length_and_dec() { set_persist_maybe_null(1); max_length=0; } }; #endif /* HAVE_DLOPEN */ @@ -1406,7 +1413,7 @@ class Item_func_get_lock :public Item_int_func Item_func_get_lock(Item *a,Item *b) :Item_int_func(a,b) {} longlong val_int(); const char *func_name() const { return "get_lock"; } - void fix_length_and_dec() { max_length=1; maybe_null=1;} + void fix_length_and_dec() { max_length=1; set_persist_maybe_null(1);} bool check_vcol_func_processor(uchar *int_arg) { return trace_unsupported_by_check_vcol_func_processor(func_name()); @@ -1420,7 +1427,7 @@ public: Item_func_release_lock(Item *a) :Item_int_func(a) {} longlong val_int(); const char *func_name() const { return "release_lock"; } - void fix_length_and_dec() { max_length=1; maybe_null=1;} + void fix_length_and_dec() { max_length=1; set_persist_maybe_null(1);} bool check_vcol_func_processor(uchar *int_arg) { return trace_unsupported_by_check_vcol_func_processor(func_name()); @@ -1437,7 +1444,7 @@ public: Item_master_pos_wait(Item *a,Item *b,Item *c) :Item_int_func(a,b,c) {} longlong val_int(); const char *func_name() const { return "master_pos_wait"; } - void fix_length_and_dec() { max_length=21; maybe_null=1;} + void fix_length_and_dec() { max_length=21; set_persist_maybe_null(1);} bool check_vcol_func_processor(uchar *int_arg) { return trace_unsupported_by_check_vcol_func_processor(func_name()); @@ -1653,7 +1660,8 @@ public: Item_func_inet_aton(Item *a) :Item_int_func(a) {} longlong val_int(); const char *func_name() const { return "inet_aton"; } - void fix_length_and_dec() { decimals= 0; max_length= 21; maybe_null= 1; unsigned_flag= 1;} + void fix_length_and_dec() + { decimals= 0; max_length= 21; set_persist_maybe_null(1); unsigned_flag= 1; } }; @@ -1722,7 +1730,8 @@ public: Item_func_is_free_lock(Item *a) :Item_int_func(a) {} longlong val_int(); const char *func_name() const { return "is_free_lock"; } - void fix_length_and_dec() { decimals=0; max_length=1; maybe_null=1;} + void fix_length_and_dec() + { decimals= 0; max_length= 1; set_persist_maybe_null(1); } bool check_vcol_func_processor(uchar *int_arg) { return trace_unsupported_by_check_vcol_func_processor(func_name()); @@ -1736,7 +1745,8 @@ public: Item_func_is_used_lock(Item *a) :Item_int_func(a) {} longlong val_int(); const char *func_name() const { return "is_used_lock"; } - void fix_length_and_dec() { decimals=0; max_length=10; maybe_null=1;} + void fix_length_and_dec() + { decimals= 0; max_length= 10; set_persist_maybe_null(1);} bool check_vcol_func_processor(uchar *int_arg) { return trace_unsupported_by_check_vcol_func_processor(func_name()); @@ -1759,7 +1769,7 @@ public: Item_func_row_count() :Item_int_func() {} longlong val_int(); const char *func_name() const { return "row_count"; } - void fix_length_and_dec() { decimals= 0; maybe_null=0; } + void fix_length_and_dec() { decimals= 0; set_persist_maybe_null(0); } bool check_vcol_func_processor(uchar *int_arg) { @@ -1893,7 +1903,7 @@ public: Item_func_found_rows() :Item_int_func() {} longlong val_int(); const char *func_name() const { return "found_rows"; } - void fix_length_and_dec() { decimals= 0; maybe_null=0; } + void fix_length_and_dec() { decimals= 0; set_persist_maybe_null(0); } bool check_vcol_func_processor(uchar *int_arg) { return trace_unsupported_by_check_vcol_func_processor(func_name()); diff --git a/sql/item_geofunc.cc b/sql/item_geofunc.cc index 522be28558f..ba330ba3b96 100644 --- a/sql/item_geofunc.cc +++ b/sql/item_geofunc.cc @@ -52,7 +52,7 @@ void Item_geometry_func::fix_length_and_dec() collation.set(&my_charset_bin); decimals=0; max_length= (uint32) 4294967295U; - maybe_null= 1; + set_persist_maybe_null(1); } @@ -145,7 +145,7 @@ void Item_func_as_wkt::fix_length_and_dec() { collation.set(default_charset(), DERIVATION_COERCIBLE, MY_REPERTOIRE_ASCII); max_length=MAX_BLOB_WIDTH; - maybe_null= 1; + set_persist_maybe_null(1); } diff --git a/sql/item_geofunc.h b/sql/item_geofunc.h index d2030ee7bb9..eae6f425f4d 100644 --- a/sql/item_geofunc.h +++ b/sql/item_geofunc.h @@ -89,7 +89,7 @@ public: { // "GeometryCollection" is the longest max_length= 20; - maybe_null= 1; + set_persist_maybe_null(1); }; }; @@ -224,7 +224,7 @@ public: { Item_func::print(str, query_type); } - void fix_length_and_dec() { maybe_null= 1; } + void fix_length_and_dec() { set_persist_maybe_null(1); } bool is_null() { (void) val_int(); return null_value; } }; @@ -251,7 +251,7 @@ public: Item_func::print(str, query_type); } - void fix_length_and_dec() { maybe_null= 1; } + void fix_length_and_dec() { set_persist_maybe_null(1); } bool is_null() { (void) val_int(); return null_value; } }; @@ -342,7 +342,7 @@ public: longlong val_int(); optimize_type select_optimize() const { return OPTIMIZE_NONE; } const char *func_name() const { return "st_isempty"; } - void fix_length_and_dec() { maybe_null= 1; } + void fix_length_and_dec() { set_persist_maybe_null(1); } }; class Item_func_issimple: public Item_bool_func @@ -356,7 +356,7 @@ public: longlong val_int(); optimize_type select_optimize() const { return OPTIMIZE_NONE; } const char *func_name() const { return "st_issimple"; } - void fix_length_and_dec() { maybe_null= 1; } + void fix_length_and_dec() { set_persist_maybe_null(1); } }; class Item_func_isclosed: public Item_bool_func @@ -366,7 +366,7 @@ public: longlong val_int(); optimize_type select_optimize() const { return OPTIMIZE_NONE; } const char *func_name() const { return "st_isclosed"; } - void fix_length_and_dec() { maybe_null= 1; } + void fix_length_and_dec() { set_persist_maybe_null(1); } }; class Item_func_dimension: public Item_int_func @@ -376,7 +376,7 @@ public: Item_func_dimension(Item *a): Item_int_func(a) {} longlong val_int(); const char *func_name() const { return "st_dimension"; } - void fix_length_and_dec() { max_length= 10; maybe_null= 1; } + void fix_length_and_dec() { max_length= 10; set_persist_maybe_null(1); } }; class Item_func_x: public Item_real_func @@ -389,7 +389,7 @@ public: void fix_length_and_dec() { Item_real_func::fix_length_and_dec(); - maybe_null= 1; + set_persist_maybe_null(1); } }; @@ -404,7 +404,7 @@ public: void fix_length_and_dec() { Item_real_func::fix_length_and_dec(); - maybe_null= 1; + set_persist_maybe_null(1); } }; @@ -416,7 +416,7 @@ public: Item_func_numgeometries(Item *a): Item_int_func(a) {} longlong val_int(); const char *func_name() const { return "st_numgeometries"; } - void fix_length_and_dec() { max_length= 10; maybe_null= 1; } + void fix_length_and_dec() { max_length= 10; set_persist_maybe_null(1); } }; @@ -427,7 +427,7 @@ public: Item_func_numinteriorring(Item *a): Item_int_func(a) {} longlong val_int(); const char *func_name() const { return "st_numinteriorrings"; } - void fix_length_and_dec() { max_length= 10; maybe_null= 1; } + void fix_length_and_dec() { max_length= 10; set_persist_maybe_null(1); } }; @@ -438,7 +438,7 @@ public: Item_func_numpoints(Item *a): Item_int_func(a) {} longlong val_int(); const char *func_name() const { return "st_numpoints"; } - void fix_length_and_dec() { max_length= 10; maybe_null= 1; } + void fix_length_and_dec() { max_length= 10; set_persist_maybe_null(1); } }; @@ -452,7 +452,7 @@ public: void fix_length_and_dec() { Item_real_func::fix_length_and_dec(); - maybe_null= 1; + set_persist_maybe_null(1); } }; @@ -467,7 +467,7 @@ public: void fix_length_and_dec() { Item_real_func::fix_length_and_dec(); - maybe_null= 1; + set_persist_maybe_null(1); } }; @@ -479,7 +479,7 @@ public: Item_func_srid(Item *a): Item_int_func(a) {} longlong val_int(); const char *func_name() const { return "srid"; } - void fix_length_and_dec() { max_length= 10; maybe_null= 1; } + void fix_length_and_dec() { max_length= 10; set_persist_maybe_null(1); } }; diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index 1b228a344f4..d0b284a363d 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -272,7 +272,7 @@ String *Item_func_aes_decrypt::val_str(String *str) void Item_func_aes_decrypt::fix_length_and_dec() { max_length=args[0]->max_length; - maybe_null= 1; + set_persist_maybe_null(1); } @@ -435,7 +435,7 @@ void Item_func_concat::fix_length_and_dec() if (max_result_length >= MAX_BLOB_WIDTH) { max_result_length= MAX_BLOB_WIDTH; - maybe_null= 1; + set_persist_maybe_null(1); } max_length= (ulong) max_result_length; } @@ -795,7 +795,7 @@ void Item_func_concat_ws::fix_length_and_dec() if (max_result_length >= MAX_BLOB_WIDTH) { max_result_length= MAX_BLOB_WIDTH; - maybe_null= 1; + set_persist_maybe_null(1); } max_length= (ulong) max_result_length; } @@ -997,7 +997,7 @@ void Item_func_replace::fix_length_and_dec() if (max_result_length >= MAX_BLOB_WIDTH) { max_result_length= MAX_BLOB_WIDTH; - maybe_null= 1; + set_persist_maybe_null(1); } max_length= (ulong) max_result_length; @@ -1081,7 +1081,7 @@ void Item_func_insert::fix_length_and_dec() if (max_result_length >= MAX_BLOB_WIDTH) { max_result_length= MAX_BLOB_WIDTH; - maybe_null= 1; + set_persist_maybe_null(1); } max_length= (ulong) max_result_length; } @@ -2184,7 +2184,7 @@ void Item_func_elt::fix_length_and_dec() set_if_bigger(max_length,args[i]->max_length); set_if_bigger(decimals,args[i]->decimals); } - maybe_null=1; // NULL if wrong first arg + set_persist_maybe_null(1); // NULL if wrong first arg } @@ -2419,14 +2419,14 @@ void Item_func_repeat::fix_length_and_dec() if (max_result_length >= MAX_BLOB_WIDTH) { max_result_length= MAX_BLOB_WIDTH; - maybe_null= 1; + set_persist_maybe_null(1); } max_length= (ulong) max_result_length; } else { max_length= MAX_BLOB_WIDTH; - maybe_null= 1; + set_persist_maybe_null(1); } } @@ -2509,14 +2509,14 @@ void Item_func_rpad::fix_length_and_dec() if (length >= MAX_BLOB_WIDTH) { length= MAX_BLOB_WIDTH; - maybe_null= 1; + set_persist_maybe_null(1); } max_length= (ulong) length; } else { max_length= MAX_BLOB_WIDTH; - maybe_null= 1; + set_persist_maybe_null(1); } } @@ -2626,14 +2626,14 @@ void Item_func_lpad::fix_length_and_dec() if (length >= MAX_BLOB_WIDTH) { length= MAX_BLOB_WIDTH; - maybe_null= 1; + set_persist_maybe_null(1); } max_length= (ulong) length; } else { max_length= MAX_BLOB_WIDTH; - maybe_null= 1; + set_persist_maybe_null(1); } } @@ -3548,7 +3548,7 @@ bool Item_func_dyncol_create::fix_fields(THD *thd, Item **ref) void Item_func_dyncol_create::fix_length_and_dec() { - maybe_null= TRUE; + set_persist_maybe_null(1); collation.set(&my_charset_bin); decimals= 0; } diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h index fc4e888e6ea..e3b040a2e55 100644 --- a/sql/item_strfunc.h +++ b/sql/item_strfunc.h @@ -319,7 +319,7 @@ public: String *val_str(String *); void fix_length_and_dec() { - maybe_null=1; + set_persist_maybe_null(1); /* 9 = MAX ((8- (arg_len % 8)) + 1) */ max_length = args[0]->max_length + 9; } @@ -335,7 +335,7 @@ public: String *val_str(String *); void fix_length_and_dec() { - maybe_null=1; + set_persist_maybe_null(1); /* 9 = MAX ((8- (arg_len % 8)) + 1) */ max_length = args[0]->max_length - 9; } @@ -361,7 +361,7 @@ public: constructor_helper(); } String *val_str(String *); - void fix_length_and_dec() { maybe_null=1; max_length = 13; } + void fix_length_and_dec() { set_persist_maybe_null(1); max_length = 13; } const char *func_name() const { return "encrypt"; } bool check_vcol_func_processor(uchar *int_arg) { @@ -431,7 +431,7 @@ public: void fix_length_and_dec() { max_length= MAX_FIELD_NAME * system_charset_info->mbmaxlen; - maybe_null=1; + set_persist_maybe_null(1); } const char *func_name() const { return "database"; } const char *fully_qualified_func_name() const { return "database()"; } @@ -607,7 +607,7 @@ public: { collation.set(default_charset()); max_length=64; - maybe_null= 1; + set_persist_maybe_null(1); } }; @@ -634,7 +634,7 @@ public: Item_func_unhex(Item *a) :Item_str_func(a) { /* there can be bad hex strings */ - maybe_null= 1; + set_persist_maybe_null(1); } const char *func_name() const { return "unhex"; } String *val_str(String *); @@ -680,7 +680,7 @@ public: void fix_length_and_dec() { collation.set(&my_charset_bin, DERIVATION_COERCIBLE); - maybe_null=1; + set_persist_maybe_null(1); max_length=MAX_BLOB_WIDTH; } bool check_vcol_func_processor(uchar *int_arg) @@ -713,7 +713,7 @@ public: { decimals= 0; max_length= 3 * 8 + 7; - maybe_null= 1; + set_persist_maybe_null(1); } }; @@ -804,7 +804,7 @@ public: { collation.set(system_charset_info); max_length= 64 * collation.collation->mbmaxlen; // should be enough - maybe_null= 0; + set_persist_maybe_null(0); }; table_map not_null_tables() const { return 0; } }; @@ -819,7 +819,7 @@ public: { collation.set(system_charset_info); max_length= 64 * collation.collation->mbmaxlen; // should be enough - maybe_null= 0; + set_persist_maybe_null(0); }; table_map not_null_tables() const { return 0; } }; @@ -865,7 +865,8 @@ class Item_func_uncompress: public Item_str_func String buffer; public: Item_func_uncompress(Item *a): Item_str_func(a){} - void fix_length_and_dec(){ maybe_null= 1; max_length= MAX_BLOB_WIDTH; } + void fix_length_and_dec() + { set_persist_maybe_null(1); max_length= MAX_BLOB_WIDTH; } const char *func_name() const{return "uncompress";} String *val_str(String *) ZLIB_DEPENDED_FUNCTION }; @@ -937,7 +938,7 @@ public: max_length= MAX_DYNAMIC_COLUMN_LENGTH; } void fix_length_and_dec() - { maybe_null= 1; } + { set_persist_maybe_null(1); } /* Mark that collation can change between calls */ bool dynamic_result() { return 1; } @@ -956,7 +957,8 @@ class Item_func_dyncol_list: public Item_str_func { public: Item_func_dyncol_list(Item *str) :Item_str_func(str) {}; - void fix_length_and_dec() { maybe_null= 1; max_length= MAX_BLOB_WIDTH; }; + void fix_length_and_dec() + { set_persist_maybe_null(1); max_length= MAX_BLOB_WIDTH; }; const char *func_name() const{ return "column_list"; } String *val_str(String *); }; diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index bdad96f12ef..eaddd39fa58 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -877,7 +877,7 @@ void Item_func_monthname::fix_length_and_dec() collation.set(cs, DERIVATION_COERCIBLE, repertoire); decimals=0; max_length= locale->max_month_name_length * collation.collation->mbmaxlen; - maybe_null=1; + set_persist_maybe_null(1); } @@ -1030,7 +1030,7 @@ void Item_func_dayname::fix_length_and_dec() collation.set(cs, DERIVATION_COERCIBLE, repertoire); decimals=0; max_length= locale->max_day_name_length * collation.collation->mbmaxlen; - maybe_null=1; + set_persist_maybe_null(1); } @@ -1404,7 +1404,7 @@ void Item_func_curdate::fix_length_and_dec() ltime.hour= ltime.minute= ltime.second= 0; ltime.time_type= MYSQL_TIMESTAMP_DATE; Item_datefunc::fix_length_and_dec(); - maybe_null= false; + set_persist_maybe_null(0); } /** @@ -1643,7 +1643,7 @@ void Item_func_date_format::fix_length_and_dec() collation.collation->mbmaxlen; set_if_smaller(max_length,MAX_BLOB_WIDTH); } - maybe_null=1; // If wrong date + set_persist_maybe_null(1); // If wrong date } @@ -1992,7 +1992,7 @@ void Item_extract::print(String *str, enum_query_type query_type) void Item_extract::fix_length_and_dec() { - maybe_null=1; // If wrong date + set_persist_maybe_null(1); // If wrong date switch (int_type) { case INTERVAL_YEAR: max_length=4; date_value=1; break; case INTERVAL_YEAR_MONTH: max_length=6; date_value=1; break; diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h index 8d19e59ddfb..25a160d91f6 100644 --- a/sql/item_timefunc.h +++ b/sql/item_timefunc.h @@ -67,7 +67,7 @@ public: { decimals=0; max_length=6*MY_CHARSET_BIN_MB_MAXLEN; - maybe_null=1; + set_persist_maybe_null(1); } enum_monotonicity_info get_monotonicity_info() const; longlong val_int_endpoint(bool left_endp, bool *incl_endp); @@ -90,7 +90,7 @@ public: { decimals=0; max_length=2*MY_CHARSET_BIN_MB_MAXLEN; - maybe_null=1; + set_persist_maybe_null(1); } bool check_partition_func_processor(uchar *int_arg) {return FALSE;} bool check_vcol_func_processor(uchar *int_arg) { return FALSE;} @@ -123,7 +123,7 @@ public: collation.set(&my_charset_bin); decimals=0; max_length=2*MY_CHARSET_BIN_MB_MAXLEN; - maybe_null=1; + set_persist_maybe_null(1); } bool check_partition_func_processor(uchar *int_arg) {return FALSE;} bool check_vcol_func_processor(uchar *int_arg) { return FALSE;} @@ -158,7 +158,7 @@ public: { decimals=0; max_length=3*MY_CHARSET_BIN_MB_MAXLEN; - maybe_null=1; + set_persist_maybe_null(1); } bool check_partition_func_processor(uchar *int_arg) {return FALSE;} bool check_vcol_func_processor(uchar *int_arg) { return FALSE;} @@ -179,7 +179,7 @@ public: { decimals=0; max_length=2*MY_CHARSET_BIN_MB_MAXLEN; - maybe_null=1; + set_persist_maybe_null(1); } bool check_partition_func_processor(uchar *int_arg) {return FALSE;} bool check_vcol_func_processor(uchar *int_arg) { return FALSE;} @@ -200,7 +200,7 @@ public: { decimals=0; max_length=2*MY_CHARSET_BIN_MB_MAXLEN; - maybe_null=1; + set_persist_maybe_null(1); } bool check_partition_func_processor(uchar *int_arg) {return FALSE;} bool check_vcol_func_processor(uchar *int_arg) { return FALSE;} @@ -221,7 +221,7 @@ public: { decimals=0; max_length=1*MY_CHARSET_BIN_MB_MAXLEN; - maybe_null=1; + set_persist_maybe_null(1); } bool check_partition_func_processor(uchar *int_arg) {return FALSE;} bool check_vcol_func_processor(uchar *int_arg) { return FALSE;} @@ -242,7 +242,7 @@ public: { decimals=0; max_length=2*MY_CHARSET_BIN_MB_MAXLEN; - maybe_null=1; + set_persist_maybe_null(1); } bool check_partition_func_processor(uchar *int_arg) {return FALSE;} bool check_vcol_func_processor(uchar *int_arg) { return FALSE;} @@ -263,7 +263,7 @@ public: { decimals=0; max_length=2*MY_CHARSET_BIN_MB_MAXLEN; - maybe_null=1; + set_persist_maybe_null(1); } }; @@ -277,7 +277,7 @@ public: { decimals=0; max_length=6*MY_CHARSET_BIN_MB_MAXLEN; - maybe_null=1; + set_persist_maybe_null(1); } bool check_partition_func_processor(uchar *int_arg) {return FALSE;} bool check_vcol_func_processor(uchar *int_arg) { return FALSE;} @@ -300,7 +300,7 @@ public: { decimals=0; max_length=4*MY_CHARSET_BIN_MB_MAXLEN; - maybe_null=1; + set_persist_maybe_null(1); } bool check_partition_func_processor(uchar *int_arg) {return FALSE;} bool check_vcol_func_processor(uchar *int_arg) { return FALSE;} @@ -335,7 +335,7 @@ public: collation.set(&my_charset_bin); decimals=0; max_length=1*MY_CHARSET_BIN_MB_MAXLEN; - maybe_null=1; + set_persist_maybe_null(1); } bool check_partition_func_processor(uchar *int_arg) {return FALSE;} bool check_vcol_func_processor(uchar *int_arg) { return FALSE;} @@ -415,7 +415,7 @@ public: const char *func_name() const { return "time_to_sec"; } void fix_num_length_and_dec() { - maybe_null= true; + set_persist_maybe_null(1); Item_func_seconds_hybrid::fix_num_length_and_dec(); } bool check_partition_func_processor(uchar *int_arg) {return FALSE;} @@ -454,7 +454,7 @@ public: { MAX_DATETIME_WIDTH, MAX_DATETIME_WIDTH, MAX_DATE_WIDTH, MAX_DATETIME_WIDTH, MIN_TIME_WIDTH }; - maybe_null= true; + set_persist_maybe_null(1); max_length= max_time_type_width[mysql_type_to_time_type(field_type())+2]; if (decimals) { @@ -470,7 +470,7 @@ public: We set maybe_null to 1 as default as any bad argument with date or time can get us to return NULL. */ - maybe_null= 1; + set_persist_maybe_null(1); } }; @@ -507,7 +507,7 @@ public: { store_now_in_TIME(<ime); Item_timefunc::fix_length_and_dec(); - maybe_null= false; + set_persist_maybe_null(0); } bool get_date(MYSQL_TIME *res, uint fuzzy_date); /* @@ -589,7 +589,7 @@ public: { store_now_in_TIME(<ime); Item_temporal_func::fix_length_and_dec(); - maybe_null= false; + set_persist_maybe_null(0); } bool get_date(MYSQL_TIME *res, uint fuzzy_date); virtual void store_now_in_TIME(MYSQL_TIME *now_time)=0; @@ -930,7 +930,7 @@ public: void fix_length_and_dec() { decimals=0; - maybe_null=1; + set_persist_maybe_null(1); } bool check_partition_func_processor(uchar *int_arg) {return FALSE;} bool check_vcol_func_processor(uchar *int_arg) { return FALSE;} @@ -952,7 +952,7 @@ public: void fix_length_and_dec() { decimals=0; - maybe_null=1; + set_persist_maybe_null(1); } virtual void print(String *str, enum_query_type query_type); }; @@ -974,7 +974,7 @@ public: const char *func_name() const { return "get_format"; } void fix_length_and_dec() { - maybe_null= 1; + set_persist_maybe_null(1); decimals=0; max_length=17*MY_CHARSET_BIN_MB_MAXLEN; } diff --git a/sql/item_xmlfunc.cc b/sql/item_xmlfunc.cc index 57995b2b643..faf5f345171 100644 --- a/sql/item_xmlfunc.cc +++ b/sql/item_xmlfunc.cc @@ -2579,6 +2579,7 @@ void Item_xml_str_func::fix_length_and_dec() int rc; nodeset_func= 0; + set_persist_maybe_null(1); if (agg_arg_charsets(collation, args, arg_count, MY_COLL_CMP_CONV, 1)) return; diff --git a/sql/item_xmlfunc.h b/sql/item_xmlfunc.h index ce33d161c79..be56a83349f 100644 --- a/sql/item_xmlfunc.h +++ b/sql/item_xmlfunc.h @@ -30,14 +30,10 @@ protected: public: Item_xml_str_func(Item *a, Item *b): Item_str_func(a,b) - { - maybe_null= TRUE; - } + {} Item_xml_str_func(Item *a, Item *b, Item *c): Item_str_func(a,b,c) - { - maybe_null= TRUE; - } + {} void fix_length_and_dec(); String *parse_xml(String *raw_xml, String *parsed_xml_buf); bool check_vcol_func_processor(uchar *int_arg) -- cgit v1.2.1 From b0ee31c89480519490537b89dca1e8cc65e2b73b Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Tue, 8 Jan 2013 21:23:03 +0100 Subject: MDEV-3942 FROM_DAYS() returns different result in MariaDB comparing to MySQL: NULL vs 0000-00-00 fixed a regression, introduced while fixing MDEV-456 --- sql/item_timefunc.cc | 8 +++----- sql/time.cc | 4 ++-- 2 files changed, 5 insertions(+), 7 deletions(-) (limited to 'sql') diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index eaddd39fa58..9f2f9b2950d 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -1,5 +1,6 @@ /* Copyright (c) 2000, 2012, Oracle and/or its affiliates. + Copyright (c) 2010, 2013, Monty Program Ab 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 @@ -1386,13 +1387,10 @@ bool Item_func_from_days::get_date(MYSQL_TIME *ltime, uint fuzzy_date) if (get_date_from_daynr((long) value, <ime->year, <ime->month, <ime->day)) - return (null_value= 1); - - if ((fuzzy_date & TIME_NO_ZERO_DATE) && ltime->year == 0) - return (null_value= 1); + return 0; ltime->time_type= MYSQL_TIMESTAMP_DATE; - return (null_value= 0); + return 0; } diff --git a/sql/time.cc b/sql/time.cc index 21ecc3f8050..3b1613282fb 100644 --- a/sql/time.cc +++ b/sql/time.cc @@ -1,6 +1,6 @@ /* Copyright (c) 2000-2007 MySQL AB, 2009 Sun Microsystems, Inc. - Copyright (c) 2009-2011 Monty Program Ab + Copyright (c) 2009-2013 Monty Program Ab Use is subject to license terms. This program is free software; you can redistribute it and/or modify @@ -153,7 +153,7 @@ bool get_date_from_daynr(long daynr,uint *ret_year,uint *ret_month, uchar *month_pos; DBUG_ENTER("get_date_from_daynr"); - if (daynr < 365 || daynr > MAX_DAY_NUMBER) + if (daynr < 366 || daynr > MAX_DAY_NUMBER) DBUG_RETURN(1); year= (uint) (daynr*100 / 36525L); -- cgit v1.2.1